@effected/yaml 0.3.1 → 0.5.0
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 +1 -1
- package/Yaml.js +102 -24
- package/YamlDocument.js +1 -1
- package/YamlEdit.js +7 -1
- package/YamlFormat.js +11 -1
- package/YamlNode.js +11 -1
- package/index.d.ts +135 -13
- package/index.js +2 -2
- package/internal/stringifier.js +52 -12
- package/package.json +4 -3
- package/tsdoc-metadata.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@effected/yaml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
[](https://nodejs.org/)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
7
|
|
|
8
8
|
Zero-dependency YAML 1.2 parsing, editing and formatting expressed as Effect schemas and pure functions. Parse a single document or a multi-document stream into plain values or an offset-preserving AST, resolve anchors and aliases, strip comments, compute byte-minimal edits, format, modify by path, walk a document as a `Stream` and decode straight into a validated domain schema.
|
|
9
9
|
|
package/Yaml.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AliasExpansionBudgetExceeded, CollectionStyle, ScalarStyle, nodeToJsValue } from "./YamlNode.js";
|
|
1
|
+
import { AliasExpansionBudgetExceeded, CollectionStyle, QuoteStyle, ScalarStyle, nodeToJsValue } from "./YamlNode.js";
|
|
2
2
|
import { buildAnchorMap } from "./internal/composer/anchors.js";
|
|
3
3
|
import { composeAllDocuments, composeFirstDocument } from "./internal/composer/document.js";
|
|
4
4
|
import { isFatalCode } from "./internal/diagnostics.js";
|
|
@@ -36,8 +36,8 @@ var YamlParseOptions = class extends Schema.Class("YamlParseOptions")({
|
|
|
36
36
|
* Options controlling stringify behavior. All fields are omissible; absent
|
|
37
37
|
* fields resolve to `indent` `2`, `lineWidth` `0`, `defaultScalarStyle`
|
|
38
38
|
* `"plain"`, `defaultCollectionStyle` `"block"`, `sortKeys` `false`,
|
|
39
|
-
* `indentSequences` `false`, `
|
|
40
|
-
* `false`.
|
|
39
|
+
* `indentSequences` `false`, `quoteStyle` `"single"`, `finalNewline` `true`
|
|
40
|
+
* and `forceDefaultStyles` `false`.
|
|
41
41
|
*
|
|
42
42
|
* `lineWidth` controls column-based scalar folding. The default `0` (and any
|
|
43
43
|
* value `<= 0`) never wraps, emitting byte-identical output to the historic
|
|
@@ -54,6 +54,18 @@ var YamlParseOptions = class extends Schema.Class("YamlParseOptions")({
|
|
|
54
54
|
* matching the `yaml` npm package's default output. Top-level sequences stay
|
|
55
55
|
* at column zero in both modes.
|
|
56
56
|
*
|
|
57
|
+
* `quoteStyle` selects the quote character used when a `plain`-styled scalar
|
|
58
|
+
* (the `defaultScalarStyle` default) turns out to require quoting: `"single"`
|
|
59
|
+
* (the default) emits `'@parcel/watcher'` — the kit's byte-compatible legacy
|
|
60
|
+
* form — while `"double"` emits `"@parcel/watcher"`, matching the `yaml` npm
|
|
61
|
+
* package's `singleQuote: false` output. It is a fallback selector only:
|
|
62
|
+
* scalars that need no quoting stay plain, and an explicit
|
|
63
|
+
* `defaultScalarStyle` of `"single-quoted"` or `"double-quoted"` still wins.
|
|
64
|
+
* On that plain fallback path, values carrying a tab, a carriage return or
|
|
65
|
+
* any other C0 control character are always emitted double-quoted whichever
|
|
66
|
+
* `quoteStyle` is set, since only double quotes can escape them into a form
|
|
67
|
+
* that round-trips exactly.
|
|
68
|
+
*
|
|
57
69
|
* Construct with the validated `YamlStringifyOptions.make({ ... })` static —
|
|
58
70
|
* the kit convention (never `new`). Call sites that take a
|
|
59
71
|
* `YamlStringifyOptions` also accept a structurally-matching plain literal.
|
|
@@ -79,7 +91,7 @@ var YamlStringifyOptions = class extends Schema.Class("YamlStringifyOptions")({
|
|
|
79
91
|
* (`>`) scalars at approximately that column, never block-literal (`|`).
|
|
80
92
|
*
|
|
81
93
|
* Takes effect only through {@link Yaml.stringify} and
|
|
82
|
-
* {@link Yaml.
|
|
94
|
+
* {@link Yaml.stringifyResult} — the two entry points that accept these
|
|
83
95
|
* options on the value path. The schema factories ({@link Yaml.fromString},
|
|
84
96
|
* {@link Yaml.schema}, {@link Yaml.YamlFromString}) encode with default
|
|
85
97
|
* stringify options (`lineWidth` `0`), so their output never folds. The
|
|
@@ -92,6 +104,17 @@ var YamlStringifyOptions = class extends Schema.Class("YamlStringifyOptions")({
|
|
|
92
104
|
defaultCollectionStyle: Schema.optionalKey(CollectionStyle),
|
|
93
105
|
sortKeys: Schema.optionalKey(Schema.Boolean),
|
|
94
106
|
indentSequences: Schema.optionalKey(Schema.Boolean),
|
|
107
|
+
/**
|
|
108
|
+
* Quote style used when a `plain`-styled scalar requires quoting. Default
|
|
109
|
+
* `"single"` — the released byte-compatible behavior. `"double"` renders
|
|
110
|
+
* the same scalars double-quoted instead, matching the `yaml` npm
|
|
111
|
+
* package's `singleQuote: false` output.
|
|
112
|
+
*
|
|
113
|
+
* Affects only the plain fallback: scalars that need no quoting stay
|
|
114
|
+
* plain, and an explicit `defaultScalarStyle` of `"single-quoted"` or
|
|
115
|
+
* `"double-quoted"` is unaffected.
|
|
116
|
+
*/
|
|
117
|
+
quoteStyle: Schema.optionalKey(QuoteStyle),
|
|
95
118
|
finalNewline: Schema.optionalKey(Schema.Boolean),
|
|
96
119
|
forceDefaultStyles: Schema.optionalKey(Schema.Boolean)
|
|
97
120
|
}) {};
|
|
@@ -141,6 +164,7 @@ const toStringifyInput = (options) => options === void 0 ? {} : {
|
|
|
141
164
|
defaultCollectionStyle: options.defaultCollectionStyle,
|
|
142
165
|
sortKeys: options.sortKeys,
|
|
143
166
|
indentSequences: options.indentSequences,
|
|
167
|
+
quoteStyle: options.quoteStyle,
|
|
144
168
|
finalNewline: options.finalNewline,
|
|
145
169
|
forceDefaultStyles: options.forceDefaultStyles
|
|
146
170
|
};
|
|
@@ -198,7 +222,7 @@ const stringifyDefectToError = (defect, value) => {
|
|
|
198
222
|
* mode (fatal diagnostics, duplicate keys, a "billion laughs" blow-up) yields
|
|
199
223
|
* a `Failure` carrying a typed {@link YamlParseError} — never a throw.
|
|
200
224
|
*/
|
|
201
|
-
const
|
|
225
|
+
const parseResultImpl = (text, options) => {
|
|
202
226
|
const doc = composeFirstDocument(text, toParseInput(options));
|
|
203
227
|
const failures = failureRecords(doc, options?.uniqueKeys ?? true);
|
|
204
228
|
if (failures.length > 0) return Result.fail(new YamlParseError({
|
|
@@ -219,7 +243,7 @@ const parseSyncImpl = (text, options) => {
|
|
|
219
243
|
* recursion budget yields a `Failure` carrying a typed {@link YamlStringifyError},
|
|
220
244
|
* never a thrown defect.
|
|
221
245
|
*/
|
|
222
|
-
const
|
|
246
|
+
const stringifyResultImpl = (value, options) => {
|
|
223
247
|
try {
|
|
224
248
|
return Result.succeed(stringifyValue(value, toStringifyInput(options)));
|
|
225
249
|
} catch (defect) {
|
|
@@ -296,17 +320,11 @@ var Yaml = class Yaml {
|
|
|
296
320
|
* resolved size grows exponentially relative to `maxAliasCount`) also
|
|
297
321
|
* fails through {@link YamlParseError} with an `AliasCountExceeded`
|
|
298
322
|
* diagnostic, never as an unhandled defect.
|
|
323
|
+
*
|
|
324
|
+
* Defined in terms of {@link Yaml.parseResult} — synchronous callers can
|
|
325
|
+
* use that variant directly.
|
|
299
326
|
*/
|
|
300
|
-
static parse = Effect.fn("Yaml.parse")(
|
|
301
|
-
const doc = composeFirstDocument(text, toParseInput(options));
|
|
302
|
-
const failures = failureRecords(doc, options?.uniqueKeys ?? true);
|
|
303
|
-
if (failures.length > 0) return yield* new YamlParseError({
|
|
304
|
-
diagnostics: toDiagnostics(text, failures),
|
|
305
|
-
input: text
|
|
306
|
-
});
|
|
307
|
-
const anchors = /* @__PURE__ */ new Map();
|
|
308
|
-
return yield* extractDocumentValue(doc.contents, anchors, options?.maxAliasCount ?? 100, text);
|
|
309
|
-
});
|
|
327
|
+
static parse = Effect.fn("Yaml.parse")((text, options) => Effect.fromResult(Yaml.parseResult(text, options)));
|
|
310
328
|
/**
|
|
311
329
|
* Parse a multi-document YAML stream into an array of plain JavaScript
|
|
312
330
|
* values (one per document, in order). Any fatal diagnostic in any
|
|
@@ -339,6 +357,15 @@ var Yaml = class Yaml {
|
|
|
339
357
|
* or on a value nested deeper than the stringifier's recursion budget
|
|
340
358
|
* (`NestingDepthExceeded`) — both surface through the typed error channel
|
|
341
359
|
* rather than as an unhandled stack-overflow defect.
|
|
360
|
+
*
|
|
361
|
+
* @remarks
|
|
362
|
+
* A `"<<"` object key is emitted **quoted** (`'<<': …`). This is the
|
|
363
|
+
* opposite of the document path ({@link YamlFormat.format} and
|
|
364
|
+
* `YamlDocument#stringify`), which leaves a parsed plain `<<` key unquoted
|
|
365
|
+
* so it keeps its merge-key meaning, and the asymmetry is deliberate: a
|
|
366
|
+
* `"<<"` key on a plain JavaScript object is an ordinary string key that
|
|
367
|
+
* never carried merge semantics, so emitting it plain would silently turn
|
|
368
|
+
* ordinary data into a merge directive.
|
|
342
369
|
*/
|
|
343
370
|
static stringify = Effect.fn("Yaml.stringify")(function* (value, options) {
|
|
344
371
|
return yield* stringifyOrFail(value, options);
|
|
@@ -346,8 +373,14 @@ var Yaml = class Yaml {
|
|
|
346
373
|
/**
|
|
347
374
|
* Synchronous single-document parse, returning a `Result` instead of
|
|
348
375
|
* an `Effect`. A pure escape hatch for config-time callers that cannot
|
|
349
|
-
* `await` an Effect (a `vitest.config.ts` is the motivating case)
|
|
350
|
-
*
|
|
376
|
+
* `await` an Effect (a `vitest.config.ts` is the motivating case).
|
|
377
|
+
*
|
|
378
|
+
* @remarks
|
|
379
|
+
* This is the package's single parse path. {@link Yaml.parse} is defined in
|
|
380
|
+
* terms of it (`Effect.fromResult` behind the named span), so the two
|
|
381
|
+
* variants cannot diverge. Reach for the `Effect` variant inside Effect
|
|
382
|
+
* code — it carries the `Yaml.parse` tracing span — and for this one at
|
|
383
|
+
* synchronous boundaries.
|
|
351
384
|
*
|
|
352
385
|
* Preserves the package contract — malformed and adversarial input fails
|
|
353
386
|
* typed, never as a defect. Fatal diagnostics, duplicate keys and a
|
|
@@ -359,7 +392,7 @@ var Yaml = class Yaml {
|
|
|
359
392
|
* import { Yaml } from "@effected/yaml";
|
|
360
393
|
* import { Result } from "effect";
|
|
361
394
|
*
|
|
362
|
-
* const result = Yaml.
|
|
395
|
+
* const result = Yaml.parseResult("name: Alice\nage: 30");
|
|
363
396
|
* if (Result.isSuccess(result)) {
|
|
364
397
|
* result.success; // { name: "Alice", age: 30 }
|
|
365
398
|
* } else {
|
|
@@ -369,8 +402,8 @@ var Yaml = class Yaml {
|
|
|
369
402
|
*
|
|
370
403
|
* @public
|
|
371
404
|
*/
|
|
372
|
-
static
|
|
373
|
-
return
|
|
405
|
+
static parseResult(text, options) {
|
|
406
|
+
return parseResultImpl(text, options);
|
|
374
407
|
}
|
|
375
408
|
/**
|
|
376
409
|
* Synchronous stringify, returning a `Result` instead of an `Effect`.
|
|
@@ -387,7 +420,7 @@ var Yaml = class Yaml {
|
|
|
387
420
|
* import { Yaml } from "@effected/yaml";
|
|
388
421
|
* import { Result } from "effect";
|
|
389
422
|
*
|
|
390
|
-
* const result = Yaml.
|
|
423
|
+
* const result = Yaml.stringifyResult({ name: "Alice" });
|
|
391
424
|
* if (Result.isFailure(result)) {
|
|
392
425
|
* result.failure; // YamlStringifyError
|
|
393
426
|
* } else {
|
|
@@ -397,8 +430,8 @@ var Yaml = class Yaml {
|
|
|
397
430
|
*
|
|
398
431
|
* @public
|
|
399
432
|
*/
|
|
400
|
-
static
|
|
401
|
-
return
|
|
433
|
+
static stringifyResult(value, options) {
|
|
434
|
+
return stringifyResultImpl(value, options);
|
|
402
435
|
}
|
|
403
436
|
/**
|
|
404
437
|
* Strip comments from YAML text. Without `replaceCh`, comment characters
|
|
@@ -527,6 +560,51 @@ var Yaml = class Yaml {
|
|
|
527
560
|
static schema(target, options) {
|
|
528
561
|
return Yaml.fromString(options).pipe(Schema.decodeTo(target));
|
|
529
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* Bind a target schema to the YAML codec once, yielding the composed
|
|
565
|
+
* schema plus pre-derived `decode`/`encode` directions — the
|
|
566
|
+
* {@link Yaml.schema} composition without the generic `Schema` machinery
|
|
567
|
+
* at every use site. Binds the plain single-document form only: default
|
|
568
|
+
* {@link YamlParseOptions} on decode, default stringify options on encode;
|
|
569
|
+
* for multi-document streams compose over {@link Yaml.allFromString}
|
|
570
|
+
* directly.
|
|
571
|
+
*
|
|
572
|
+
* Both directions fail with `Schema.SchemaError`, exactly as
|
|
573
|
+
* `Schema.decodeEffect`/`Schema.encodeEffect` over {@link Yaml.schema}
|
|
574
|
+
* would; the target's decoding/encoding service requirements flow through.
|
|
575
|
+
*
|
|
576
|
+
* @remarks
|
|
577
|
+
* Schema-producing: each call composes a fresh schema and derives both
|
|
578
|
+
* directions from it. Bind the result to a `const` — that single binding is
|
|
579
|
+
* the point.
|
|
580
|
+
*
|
|
581
|
+
* @example
|
|
582
|
+
* ```ts
|
|
583
|
+
* import { Yaml } from "@effected/yaml";
|
|
584
|
+
* import { Effect, Schema } from "effect";
|
|
585
|
+
*
|
|
586
|
+
* const Config = Schema.Struct({ port: Schema.Number });
|
|
587
|
+
* const config = Yaml.bind(Config);
|
|
588
|
+
*
|
|
589
|
+
* const program = Effect.gen(function* () {
|
|
590
|
+
* const value = yield* config.decode("port: 3000");
|
|
591
|
+
* const text = yield* config.encode(value);
|
|
592
|
+
* return [value, text] as const;
|
|
593
|
+
* });
|
|
594
|
+
* ```
|
|
595
|
+
*
|
|
596
|
+
* @param target - The domain schema decoded values must satisfy.
|
|
597
|
+
* @returns A {@link YamlBoundCodec} carrying the composed schema and its
|
|
598
|
+
* two pre-bound directions.
|
|
599
|
+
*/
|
|
600
|
+
static bind(target) {
|
|
601
|
+
const schema = Yaml.schema(target);
|
|
602
|
+
return {
|
|
603
|
+
schema,
|
|
604
|
+
decode: Schema.decodeEffect(schema),
|
|
605
|
+
encode: Schema.encodeEffect(schema)
|
|
606
|
+
};
|
|
607
|
+
}
|
|
530
608
|
};
|
|
531
609
|
/**
|
|
532
610
|
* Parse for `equals`/`equalsValue`: any recorded error — fatal or not — or a
|
package/YamlDocument.js
CHANGED
|
@@ -96,7 +96,7 @@ var YamlDocument = class YamlDocument extends Schema.Class("YamlDocument")({
|
|
|
96
96
|
* `YamlStringifyOptions.lineWidth` is not honored here: column-based
|
|
97
97
|
* scalar folding exists only on the value path, through the entry points
|
|
98
98
|
* that accept stringify options ({@link Yaml.stringify} and
|
|
99
|
-
* {@link Yaml.
|
|
99
|
+
* {@link Yaml.stringifyResult}). The
|
|
100
100
|
* document/node path threads `lineWidth` into its render context but
|
|
101
101
|
* never reads it, so long scalars are emitted unfolded regardless of the
|
|
102
102
|
* option. Callers that need folding should render the plain value
|
package/YamlEdit.js
CHANGED
|
@@ -31,10 +31,16 @@ var YamlEdit = class extends Schema.Class("YamlEdit")({
|
|
|
31
31
|
/**
|
|
32
32
|
* Apply `edits` to `text`, producing a new string. Edits are applied in
|
|
33
33
|
* reverse-offset order so earlier offsets stay valid; the input `edits`
|
|
34
|
-
* array is not mutated.
|
|
34
|
+
* array is not mutated. Overlapping edits are a programmer error and throw
|
|
35
|
+
* as a defect — `YamlFormat` never produces them.
|
|
35
36
|
*/
|
|
36
37
|
static applyAll(text, edits) {
|
|
37
38
|
const sorted = [...edits].sort((a, b) => b.offset - a.offset);
|
|
39
|
+
for (let i = 0; i + 1 < sorted.length; i++) {
|
|
40
|
+
const upper = sorted[i];
|
|
41
|
+
const lower = sorted[i + 1];
|
|
42
|
+
if (lower.offset + lower.length > upper.offset) throw new Error(`YamlEdit.applyAll received overlapping edits at offsets ${lower.offset} and ${upper.offset} — overlapping edits are a programmer error`);
|
|
43
|
+
}
|
|
38
44
|
let result = text;
|
|
39
45
|
for (const edit of sorted) result = result.substring(0, edit.offset) + edit.content + result.substring(edit.offset + edit.length);
|
|
40
46
|
return result;
|
package/YamlFormat.js
CHANGED
|
@@ -11,7 +11,8 @@ import { Effect, Schema } from "effect";
|
|
|
11
11
|
//#region src/YamlFormat.ts
|
|
12
12
|
/**
|
|
13
13
|
* Options controlling formatting behavior: every {@link YamlStringifyOptions}
|
|
14
|
-
* field (derived, not hand-duplicated — including `indentSequences`
|
|
14
|
+
* field (derived, not hand-duplicated — including `indentSequences` and
|
|
15
|
+
* `quoteStyle`) plus
|
|
15
16
|
* `preserveComments` (default `true`) and `range` (restrict edits to a
|
|
16
17
|
* region; see the module-level remarks on the `range` parameter vs. this
|
|
17
18
|
* field).
|
|
@@ -78,6 +79,7 @@ const toStringifyInput = (options) => options === void 0 ? {} : {
|
|
|
78
79
|
defaultCollectionStyle: options.defaultCollectionStyle,
|
|
79
80
|
sortKeys: options.sortKeys,
|
|
80
81
|
indentSequences: options.indentSequences,
|
|
82
|
+
quoteStyle: options.quoteStyle,
|
|
81
83
|
finalNewline: options.finalNewline,
|
|
82
84
|
forceDefaultStyles: options.forceDefaultStyles
|
|
83
85
|
};
|
|
@@ -251,6 +253,14 @@ var YamlFormat = class YamlFormat {
|
|
|
251
253
|
* `options?.range` when both are given; either accepts a plain
|
|
252
254
|
* `{ offset, length }` object as well as a {@link YamlRange} instance, so
|
|
253
255
|
* callers do not need `YamlRange.make(...)` for the common case.
|
|
256
|
+
*
|
|
257
|
+
* A plain `<<` mapping key is preserved unquoted, keeping its merge-key
|
|
258
|
+
* meaning (`tag:yaml.org,2002:merge`) — quoting it to `'<<'` would produce
|
|
259
|
+
* an ordinary string key that merges nothing, changing what the document
|
|
260
|
+
* means with no error raised. A key the author quoted explicitly keeps its
|
|
261
|
+
* quotes, since that is a literal string key they wrote deliberately. Note
|
|
262
|
+
* that {@link Yaml.stringify} is deliberately the other way round for a
|
|
263
|
+
* `"<<"` key on a plain JavaScript object.
|
|
254
264
|
*/
|
|
255
265
|
static format(text, range, options) {
|
|
256
266
|
const formatted = formatDocument(text, options);
|
package/YamlNode.js
CHANGED
|
@@ -20,6 +20,16 @@ const ScalarStyle = Schema.Literals([
|
|
|
20
20
|
*/
|
|
21
21
|
const CollectionStyle = Schema.Literals(["block", "flow"]);
|
|
22
22
|
/**
|
|
23
|
+
* Quote characters available to the stringifier's plain-scalar fallback: the
|
|
24
|
+
* style a `plain`-styled scalar is rendered in when it turns out to require
|
|
25
|
+
* quoting. Referenced by the `quoteStyle` field of `YamlStringifyOptions`;
|
|
26
|
+
* unlike `ScalarStyle` it is a stringify-option vocabulary, never a property
|
|
27
|
+
* of a composed node.
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
const QuoteStyle = Schema.Literals(["single", "double"]);
|
|
32
|
+
/**
|
|
23
33
|
* Block-scalar chomping indicators (`-` strip, default clip, `+` keep).
|
|
24
34
|
* Referenced by the {@link YamlScalar} `chomp` field schema.
|
|
25
35
|
*
|
|
@@ -393,4 +403,4 @@ function nodeToValue(node, anchors, budget, counting = false) {
|
|
|
393
403
|
}
|
|
394
404
|
|
|
395
405
|
//#endregion
|
|
396
|
-
export { AliasExpansionBudgetExceeded, CollectionStyle, ScalarChomp, ScalarStyle, YamlAlias, YamlMap, YamlNode, YamlPair, YamlScalar, YamlSeq, aliasExpansionLimit, nodeToJsValue };
|
|
406
|
+
export { AliasExpansionBudgetExceeded, CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, YamlAlias, YamlMap, YamlNode, YamlPair, YamlScalar, YamlSeq, aliasExpansionLimit, nodeToJsValue };
|
package/index.d.ts
CHANGED
|
@@ -151,7 +151,7 @@ declare const YamlStringifyOptions_base: Schema.Class<YamlStringifyOptions, Sche
|
|
|
151
151
|
* (`>`) scalars at approximately that column, never block-literal (`|`).
|
|
152
152
|
*
|
|
153
153
|
* Takes effect only through {@link Yaml.stringify} and
|
|
154
|
-
* {@link Yaml.
|
|
154
|
+
* {@link Yaml.stringifyResult} — the two entry points that accept these
|
|
155
155
|
* options on the value path. The schema factories ({@link Yaml.fromString},
|
|
156
156
|
* {@link Yaml.schema}, {@link Yaml.YamlFromString}) encode with default
|
|
157
157
|
* stringify options (`lineWidth` `0`), so their output never folds. The
|
|
@@ -164,6 +164,17 @@ declare const YamlStringifyOptions_base: Schema.Class<YamlStringifyOptions, Sche
|
|
|
164
164
|
readonly defaultCollectionStyle: Schema.optionalKey<Schema.Literals<readonly ["block", "flow"]>>;
|
|
165
165
|
readonly sortKeys: Schema.optionalKey<Schema.Boolean>;
|
|
166
166
|
readonly indentSequences: Schema.optionalKey<Schema.Boolean>;
|
|
167
|
+
/**
|
|
168
|
+
* Quote style used when a `plain`-styled scalar requires quoting. Default
|
|
169
|
+
* `"single"` — the released byte-compatible behavior. `"double"` renders
|
|
170
|
+
* the same scalars double-quoted instead, matching the `yaml` npm
|
|
171
|
+
* package's `singleQuote: false` output.
|
|
172
|
+
*
|
|
173
|
+
* Affects only the plain fallback: scalars that need no quoting stay
|
|
174
|
+
* plain, and an explicit `defaultScalarStyle` of `"single-quoted"` or
|
|
175
|
+
* `"double-quoted"` is unaffected.
|
|
176
|
+
*/
|
|
177
|
+
readonly quoteStyle: Schema.optionalKey<Schema.Literals<readonly ["single", "double"]>>;
|
|
167
178
|
readonly finalNewline: Schema.optionalKey<Schema.Boolean>;
|
|
168
179
|
readonly forceDefaultStyles: Schema.optionalKey<Schema.Boolean>;
|
|
169
180
|
}>, {}>;
|
|
@@ -171,8 +182,8 @@ declare const YamlStringifyOptions_base: Schema.Class<YamlStringifyOptions, Sche
|
|
|
171
182
|
* Options controlling stringify behavior. All fields are omissible; absent
|
|
172
183
|
* fields resolve to `indent` `2`, `lineWidth` `0`, `defaultScalarStyle`
|
|
173
184
|
* `"plain"`, `defaultCollectionStyle` `"block"`, `sortKeys` `false`,
|
|
174
|
-
* `indentSequences` `false`, `
|
|
175
|
-
* `false`.
|
|
185
|
+
* `indentSequences` `false`, `quoteStyle` `"single"`, `finalNewline` `true`
|
|
186
|
+
* and `forceDefaultStyles` `false`.
|
|
176
187
|
*
|
|
177
188
|
* `lineWidth` controls column-based scalar folding. The default `0` (and any
|
|
178
189
|
* value `<= 0`) never wraps, emitting byte-identical output to the historic
|
|
@@ -189,6 +200,18 @@ declare const YamlStringifyOptions_base: Schema.Class<YamlStringifyOptions, Sche
|
|
|
189
200
|
* matching the `yaml` npm package's default output. Top-level sequences stay
|
|
190
201
|
* at column zero in both modes.
|
|
191
202
|
*
|
|
203
|
+
* `quoteStyle` selects the quote character used when a `plain`-styled scalar
|
|
204
|
+
* (the `defaultScalarStyle` default) turns out to require quoting: `"single"`
|
|
205
|
+
* (the default) emits `'@parcel/watcher'` — the kit's byte-compatible legacy
|
|
206
|
+
* form — while `"double"` emits `"@parcel/watcher"`, matching the `yaml` npm
|
|
207
|
+
* package's `singleQuote: false` output. It is a fallback selector only:
|
|
208
|
+
* scalars that need no quoting stay plain, and an explicit
|
|
209
|
+
* `defaultScalarStyle` of `"single-quoted"` or `"double-quoted"` still wins.
|
|
210
|
+
* On that plain fallback path, values carrying a tab, a carriage return or
|
|
211
|
+
* any other C0 control character are always emitted double-quoted whichever
|
|
212
|
+
* `quoteStyle` is set, since only double quotes can escape them into a form
|
|
213
|
+
* that round-trips exactly.
|
|
214
|
+
*
|
|
192
215
|
* Construct with the validated `YamlStringifyOptions.make({ ... })` static —
|
|
193
216
|
* the kit convention (never `new`). Call sites that take a
|
|
194
217
|
* `YamlStringifyOptions` also accept a structurally-matching plain literal.
|
|
@@ -237,6 +260,22 @@ declare const YamlStringifyError_base: Schema.Class<YamlStringifyError, Schema.T
|
|
|
237
260
|
declare class YamlStringifyError extends YamlStringifyError_base {
|
|
238
261
|
get message(): string;
|
|
239
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* A domain codec pre-bound to its two directions, returned by
|
|
265
|
+
* {@link Yaml.bind}: the composed `schema` (what {@link Yaml.schema} returns)
|
|
266
|
+
* plus `decode` and `encode` functions derived from it once, so callers need
|
|
267
|
+
* no generic `Schema` machinery at the use site.
|
|
268
|
+
*
|
|
269
|
+
* @public
|
|
270
|
+
*/
|
|
271
|
+
interface YamlBoundCodec<T, RD = never, RE = never> {
|
|
272
|
+
/** The composed codec decoding a YAML `string` straight into `T`. */
|
|
273
|
+
readonly schema: Schema.Codec<T, string, RD, RE>;
|
|
274
|
+
/** Decode a single-document YAML string into a validated `T`. */
|
|
275
|
+
readonly decode: (text: string) => Effect.Effect<T, Schema.SchemaError, RD>;
|
|
276
|
+
/** Encode a `T` back to YAML text with default stringify options. */
|
|
277
|
+
readonly encode: (value: T) => Effect.Effect<string, Schema.SchemaError, RE>;
|
|
278
|
+
}
|
|
240
279
|
/**
|
|
241
280
|
* Static entry points for YAML parsing, stringification, comment stripping,
|
|
242
281
|
* semantic equality and the schema factories. Not instantiable.
|
|
@@ -273,6 +312,9 @@ declare class Yaml {
|
|
|
273
312
|
* resolved size grows exponentially relative to `maxAliasCount`) also
|
|
274
313
|
* fails through {@link YamlParseError} with an `AliasCountExceeded`
|
|
275
314
|
* diagnostic, never as an unhandled defect.
|
|
315
|
+
*
|
|
316
|
+
* Defined in terms of {@link Yaml.parseResult} — synchronous callers can
|
|
317
|
+
* use that variant directly.
|
|
276
318
|
*/
|
|
277
319
|
static readonly parse: (text: string, options?: YamlParseOptions | undefined) => Effect.Effect<unknown, YamlParseError, never>;
|
|
278
320
|
/**
|
|
@@ -292,13 +334,28 @@ declare class Yaml {
|
|
|
292
334
|
* or on a value nested deeper than the stringifier's recursion budget
|
|
293
335
|
* (`NestingDepthExceeded`) — both surface through the typed error channel
|
|
294
336
|
* rather than as an unhandled stack-overflow defect.
|
|
337
|
+
*
|
|
338
|
+
* @remarks
|
|
339
|
+
* A `"<<"` object key is emitted **quoted** (`'<<': …`). This is the
|
|
340
|
+
* opposite of the document path ({@link YamlFormat.format} and
|
|
341
|
+
* `YamlDocument#stringify`), which leaves a parsed plain `<<` key unquoted
|
|
342
|
+
* so it keeps its merge-key meaning, and the asymmetry is deliberate: a
|
|
343
|
+
* `"<<"` key on a plain JavaScript object is an ordinary string key that
|
|
344
|
+
* never carried merge semantics, so emitting it plain would silently turn
|
|
345
|
+
* ordinary data into a merge directive.
|
|
295
346
|
*/
|
|
296
347
|
static readonly stringify: (value: unknown, options?: YamlStringifyOptions | undefined) => Effect.Effect<string, YamlStringifyError, never>;
|
|
297
348
|
/**
|
|
298
349
|
* Synchronous single-document parse, returning a `Result` instead of
|
|
299
350
|
* an `Effect`. A pure escape hatch for config-time callers that cannot
|
|
300
|
-
* `await` an Effect (a `vitest.config.ts` is the motivating case)
|
|
301
|
-
*
|
|
351
|
+
* `await` an Effect (a `vitest.config.ts` is the motivating case).
|
|
352
|
+
*
|
|
353
|
+
* @remarks
|
|
354
|
+
* This is the package's single parse path. {@link Yaml.parse} is defined in
|
|
355
|
+
* terms of it (`Effect.fromResult` behind the named span), so the two
|
|
356
|
+
* variants cannot diverge. Reach for the `Effect` variant inside Effect
|
|
357
|
+
* code — it carries the `Yaml.parse` tracing span — and for this one at
|
|
358
|
+
* synchronous boundaries.
|
|
302
359
|
*
|
|
303
360
|
* Preserves the package contract — malformed and adversarial input fails
|
|
304
361
|
* typed, never as a defect. Fatal diagnostics, duplicate keys and a
|
|
@@ -310,7 +367,7 @@ declare class Yaml {
|
|
|
310
367
|
* import { Yaml } from "@effected/yaml";
|
|
311
368
|
* import { Result } from "effect";
|
|
312
369
|
*
|
|
313
|
-
* const result = Yaml.
|
|
370
|
+
* const result = Yaml.parseResult("name: Alice\nage: 30");
|
|
314
371
|
* if (Result.isSuccess(result)) {
|
|
315
372
|
* result.success; // { name: "Alice", age: 30 }
|
|
316
373
|
* } else {
|
|
@@ -320,7 +377,7 @@ declare class Yaml {
|
|
|
320
377
|
*
|
|
321
378
|
* @public
|
|
322
379
|
*/
|
|
323
|
-
static
|
|
380
|
+
static parseResult(text: string, options?: YamlParseOptions): Result.Result<unknown, YamlParseError>;
|
|
324
381
|
/**
|
|
325
382
|
* Synchronous stringify, returning a `Result` instead of an `Effect`.
|
|
326
383
|
* The pure counterpart to {@link Yaml.stringify} for config-time callers
|
|
@@ -336,7 +393,7 @@ declare class Yaml {
|
|
|
336
393
|
* import { Yaml } from "@effected/yaml";
|
|
337
394
|
* import { Result } from "effect";
|
|
338
395
|
*
|
|
339
|
-
* const result = Yaml.
|
|
396
|
+
* const result = Yaml.stringifyResult({ name: "Alice" });
|
|
340
397
|
* if (Result.isFailure(result)) {
|
|
341
398
|
* result.failure; // YamlStringifyError
|
|
342
399
|
* } else {
|
|
@@ -346,7 +403,7 @@ declare class Yaml {
|
|
|
346
403
|
*
|
|
347
404
|
* @public
|
|
348
405
|
*/
|
|
349
|
-
static
|
|
406
|
+
static stringifyResult(value: unknown, options?: YamlStringifyOptions): Result.Result<string, YamlStringifyError>;
|
|
350
407
|
/**
|
|
351
408
|
* Strip comments from YAML text. Without `replaceCh`, comment characters
|
|
352
409
|
* are removed (line breaks are kept, so line numbers stay stable); with a
|
|
@@ -404,6 +461,44 @@ declare class Yaml {
|
|
|
404
461
|
* {@link Yaml.fromString}).
|
|
405
462
|
*/
|
|
406
463
|
static schema<T, E, RD = never, RE = never>(target: Schema.Codec<T, E, RD, RE>, options?: YamlParseOptions): Schema.Codec<T, string, RD, RE>;
|
|
464
|
+
/**
|
|
465
|
+
* Bind a target schema to the YAML codec once, yielding the composed
|
|
466
|
+
* schema plus pre-derived `decode`/`encode` directions — the
|
|
467
|
+
* {@link Yaml.schema} composition without the generic `Schema` machinery
|
|
468
|
+
* at every use site. Binds the plain single-document form only: default
|
|
469
|
+
* {@link YamlParseOptions} on decode, default stringify options on encode;
|
|
470
|
+
* for multi-document streams compose over {@link Yaml.allFromString}
|
|
471
|
+
* directly.
|
|
472
|
+
*
|
|
473
|
+
* Both directions fail with `Schema.SchemaError`, exactly as
|
|
474
|
+
* `Schema.decodeEffect`/`Schema.encodeEffect` over {@link Yaml.schema}
|
|
475
|
+
* would; the target's decoding/encoding service requirements flow through.
|
|
476
|
+
*
|
|
477
|
+
* @remarks
|
|
478
|
+
* Schema-producing: each call composes a fresh schema and derives both
|
|
479
|
+
* directions from it. Bind the result to a `const` — that single binding is
|
|
480
|
+
* the point.
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* import { Yaml } from "@effected/yaml";
|
|
485
|
+
* import { Effect, Schema } from "effect";
|
|
486
|
+
*
|
|
487
|
+
* const Config = Schema.Struct({ port: Schema.Number });
|
|
488
|
+
* const config = Yaml.bind(Config);
|
|
489
|
+
*
|
|
490
|
+
* const program = Effect.gen(function* () {
|
|
491
|
+
* const value = yield* config.decode("port: 3000");
|
|
492
|
+
* const text = yield* config.encode(value);
|
|
493
|
+
* return [value, text] as const;
|
|
494
|
+
* });
|
|
495
|
+
* ```
|
|
496
|
+
*
|
|
497
|
+
* @param target - The domain schema decoded values must satisfy.
|
|
498
|
+
* @returns A {@link YamlBoundCodec} carrying the composed schema and its
|
|
499
|
+
* two pre-bound directions.
|
|
500
|
+
*/
|
|
501
|
+
static bind<T, E, RD = never, RE = never>(target: Schema.Codec<T, E, RD, RE>): YamlBoundCodec<T, RD, RE>;
|
|
407
502
|
}
|
|
408
503
|
//#endregion
|
|
409
504
|
//#region src/YamlEdit.d.ts
|
|
@@ -453,7 +548,8 @@ declare class YamlEdit extends YamlEdit_base {
|
|
|
453
548
|
/**
|
|
454
549
|
* Apply `edits` to `text`, producing a new string. Edits are applied in
|
|
455
550
|
* reverse-offset order so earlier offsets stay valid; the input `edits`
|
|
456
|
-
* array is not mutated.
|
|
551
|
+
* array is not mutated. Overlapping edits are a programmer error and throw
|
|
552
|
+
* as a defect — `YamlFormat` never produces them.
|
|
457
553
|
*/
|
|
458
554
|
static applyAll(text: string, edits: ReadonlyArray<YamlEdit>): string;
|
|
459
555
|
}
|
|
@@ -483,6 +579,22 @@ declare const CollectionStyle: Schema.Literals<readonly ["block", "flow"]>;
|
|
|
483
579
|
* @public
|
|
484
580
|
*/
|
|
485
581
|
type CollectionStyle = typeof CollectionStyle.Type;
|
|
582
|
+
/**
|
|
583
|
+
* Quote characters available to the stringifier's plain-scalar fallback: the
|
|
584
|
+
* style a `plain`-styled scalar is rendered in when it turns out to require
|
|
585
|
+
* quoting. Referenced by the `quoteStyle` field of `YamlStringifyOptions`;
|
|
586
|
+
* unlike `ScalarStyle` it is a stringify-option vocabulary, never a property
|
|
587
|
+
* of a composed node.
|
|
588
|
+
*
|
|
589
|
+
* @public
|
|
590
|
+
*/
|
|
591
|
+
declare const QuoteStyle: Schema.Literals<readonly ["single", "double"]>;
|
|
592
|
+
/**
|
|
593
|
+
* The union of all fallback quote style string literals.
|
|
594
|
+
*
|
|
595
|
+
* @public
|
|
596
|
+
*/
|
|
597
|
+
type QuoteStyle = typeof QuoteStyle.Type;
|
|
486
598
|
/**
|
|
487
599
|
* Block-scalar chomping indicators (`-` strip, default clip, `+` keep).
|
|
488
600
|
* Referenced by the {@link YamlScalar} `chomp` field schema.
|
|
@@ -736,7 +848,7 @@ declare class YamlDocument extends YamlDocument_base {
|
|
|
736
848
|
* `YamlStringifyOptions.lineWidth` is not honored here: column-based
|
|
737
849
|
* scalar folding exists only on the value path, through the entry points
|
|
738
850
|
* that accept stringify options ({@link Yaml.stringify} and
|
|
739
|
-
* {@link Yaml.
|
|
851
|
+
* {@link Yaml.stringifyResult}). The
|
|
740
852
|
* document/node path threads `lineWidth` into its render context but
|
|
741
853
|
* never reads it, so long scalars are emitted unfolded regardless of the
|
|
742
854
|
* option. Callers that need folding should render the plain value
|
|
@@ -771,6 +883,7 @@ declare const YamlFormattingOptions_base: Schema.Class<YamlFormattingOptions, Sc
|
|
|
771
883
|
readonly defaultCollectionStyle: Schema.optionalKey<Schema.Literals<readonly ["block", "flow"]>>;
|
|
772
884
|
readonly sortKeys: Schema.optionalKey<Schema.Boolean>;
|
|
773
885
|
readonly indentSequences: Schema.optionalKey<Schema.Boolean>;
|
|
886
|
+
readonly quoteStyle: Schema.optionalKey<Schema.Literals<readonly ["single", "double"]>>;
|
|
774
887
|
readonly finalNewline: Schema.optionalKey<Schema.Boolean>;
|
|
775
888
|
readonly forceDefaultStyles: Schema.optionalKey<Schema.Boolean>;
|
|
776
889
|
readonly preserveComments: Schema.optionalKey<Schema.Boolean>;
|
|
@@ -778,7 +891,8 @@ declare const YamlFormattingOptions_base: Schema.Class<YamlFormattingOptions, Sc
|
|
|
778
891
|
}>, {}>;
|
|
779
892
|
/**
|
|
780
893
|
* Options controlling formatting behavior: every {@link YamlStringifyOptions}
|
|
781
|
-
* field (derived, not hand-duplicated — including `indentSequences`
|
|
894
|
+
* field (derived, not hand-duplicated — including `indentSequences` and
|
|
895
|
+
* `quoteStyle`) plus
|
|
782
896
|
* `preserveComments` (default `true`) and `range` (restrict edits to a
|
|
783
897
|
* region; see the module-level remarks on the `range` parameter vs. this
|
|
784
898
|
* field).
|
|
@@ -843,6 +957,14 @@ declare class YamlFormat {
|
|
|
843
957
|
* `options?.range` when both are given; either accepts a plain
|
|
844
958
|
* `{ offset, length }` object as well as a {@link YamlRange} instance, so
|
|
845
959
|
* callers do not need `YamlRange.make(...)` for the common case.
|
|
960
|
+
*
|
|
961
|
+
* A plain `<<` mapping key is preserved unquoted, keeping its merge-key
|
|
962
|
+
* meaning (`tag:yaml.org,2002:merge`) — quoting it to `'<<'` would produce
|
|
963
|
+
* an ordinary string key that merges nothing, changing what the document
|
|
964
|
+
* means with no error raised. A key the author quoted explicitly keeps its
|
|
965
|
+
* quotes, since that is a literal string key they wrote deliberately. Note
|
|
966
|
+
* that {@link Yaml.stringify} is deliberately the other way round for a
|
|
967
|
+
* `"<<"` key on a plain JavaScript object.
|
|
846
968
|
*/
|
|
847
969
|
static format(text: string, range?: YamlRangeLike, options?: YamlFormattingOptions): ReadonlyArray<YamlEdit>;
|
|
848
970
|
/**
|
|
@@ -1466,5 +1588,5 @@ declare class YamlVisitor {
|
|
|
1466
1588
|
static visit(text: string, options?: YamlParseOptions): Stream.Stream<YamlVisitorEvent>;
|
|
1467
1589
|
}
|
|
1468
1590
|
//#endregion
|
|
1469
|
-
export { CollectionStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, type YamlPath, YamlRange, type YamlRangeLike, YamlScalar, type YamlSegment, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlVisitor, YamlVisitorEvent };
|
|
1591
|
+
export { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, type YamlBoundCodec, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, type YamlPath, YamlRange, type YamlRangeLike, YamlScalar, type YamlSegment, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlVisitor, YamlVisitorEvent };
|
|
1470
1592
|
//# sourceMappingURL=index.d.ts.map
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CollectionStyle, ScalarChomp, ScalarStyle, YamlAlias, YamlMap, YamlNode, YamlPair, YamlScalar, YamlSeq } from "./YamlNode.js";
|
|
1
|
+
import { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, YamlAlias, YamlMap, YamlNode, YamlPair, YamlScalar, YamlSeq } from "./YamlNode.js";
|
|
2
2
|
import { YamlComposerErrorCode, YamlDiagnostic, YamlErrorCode, YamlLexErrorCode, YamlModifyErrorCode, YamlParseErrorCode, YamlStringifyErrorCode } from "./YamlDiagnostic.js";
|
|
3
3
|
import { Yaml, YamlParseError, YamlParseOptions, YamlStringifyError, YamlStringifyOptions } from "./Yaml.js";
|
|
4
4
|
import { YamlDirective, YamlDocument } from "./YamlDocument.js";
|
|
@@ -6,4 +6,4 @@ import { YamlEdit, YamlRange } from "./YamlEdit.js";
|
|
|
6
6
|
import { YamlFormat, YamlFormattingOptions, YamlModificationError } from "./YamlFormat.js";
|
|
7
7
|
import { YamlVisitor, YamlVisitorEvent } from "./YamlVisitor.js";
|
|
8
8
|
|
|
9
|
-
export { CollectionStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, YamlRange, YamlScalar, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlVisitor, YamlVisitorEvent };
|
|
9
|
+
export { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, YamlRange, YamlScalar, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlVisitor, YamlVisitorEvent };
|
package/internal/stringifier.js
CHANGED
|
@@ -107,7 +107,10 @@ function requiresQuoting(s, ignoreType = false) {
|
|
|
107
107
|
if (s.includes(" #") || s.includes(" #")) return true;
|
|
108
108
|
const last = s[s.length - 1];
|
|
109
109
|
if (last === " " || last === " ") return true;
|
|
110
|
-
for (let i = 0; i < s.length; i++)
|
|
110
|
+
for (let i = 0; i < s.length; i++) {
|
|
111
|
+
const code = s.charCodeAt(i);
|
|
112
|
+
if (code === 9 || code === 13 || isControlChar(code)) return true;
|
|
113
|
+
}
|
|
111
114
|
return false;
|
|
112
115
|
}
|
|
113
116
|
/**
|
|
@@ -164,11 +167,13 @@ function renderSingleQuoted(s) {
|
|
|
164
167
|
*
|
|
165
168
|
* Multi-line strings are routed to block styles regardless of the requested
|
|
166
169
|
* style (except double-quoted). For single-line strings, plain style delegates
|
|
167
|
-
* to {@link requiresQuoting} and falls back to
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
+
* to {@link requiresQuoting} and falls back to `quoteStyle` (single-quoted by
|
|
171
|
+
* default, double-quoted when the caller asked for it) — or to double-quoted
|
|
172
|
+
* regardless when the value needs YAML escapes single quotes cannot express.
|
|
173
|
+
* Block literal and block folded styles are always accepted for single-line
|
|
174
|
+
* strings even though the output is unusual.
|
|
170
175
|
*/
|
|
171
|
-
function renderString(s, style, indent, ignoreType = false, canonical = false, explicitChomp, parentPosition) {
|
|
176
|
+
function renderString(s, style, indent, ignoreType = false, canonical = false, explicitChomp, parentPosition, quoteStyle = "single") {
|
|
172
177
|
if (s.includes("\n")) {
|
|
173
178
|
let hasControl = false;
|
|
174
179
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -202,7 +207,7 @@ function renderString(s, style, indent, ignoreType = false, canonical = false, e
|
|
|
202
207
|
if (requiresQuoting(s, ignoreType)) {
|
|
203
208
|
if (s.includes(" ") || s.includes("\r")) return renderDoubleQuoted(s, canonical);
|
|
204
209
|
for (let i = 0; i < s.length; i++) if (isControlChar(s.charCodeAt(i))) return renderDoubleQuoted(s, canonical);
|
|
205
|
-
return renderSingleQuoted(s);
|
|
210
|
+
return quoteStyle === "double" ? renderDoubleQuoted(s, canonical) : renderSingleQuoted(s);
|
|
206
211
|
}
|
|
207
212
|
return s;
|
|
208
213
|
case "single-quoted": return renderSingleQuoted(s);
|
|
@@ -257,6 +262,7 @@ function createContext(options) {
|
|
|
257
262
|
defaultCollectionStyle: options?.defaultCollectionStyle ?? "block",
|
|
258
263
|
sortKeys: options?.sortKeys ?? false,
|
|
259
264
|
indentSequences: options?.indentSequences ?? false,
|
|
265
|
+
quoteStyle: options?.quoteStyle ?? "single",
|
|
260
266
|
forceDefaultStyles: options?.forceDefaultStyles ?? false,
|
|
261
267
|
seen: /* @__PURE__ */ new Set()
|
|
262
268
|
};
|
|
@@ -276,7 +282,7 @@ function stringifyLines(value, ctx, depth, allowFold = true) {
|
|
|
276
282
|
if (typeof value === "bigint") return [value.toString()];
|
|
277
283
|
if (typeof value === "string") {
|
|
278
284
|
const indentStr = " ".repeat(ctx.indent);
|
|
279
|
-
const rendered = renderString(value, ctx.defaultScalarStyle, indentStr, false, ctx.forceDefaultStyles);
|
|
285
|
+
const rendered = renderString(value, ctx.defaultScalarStyle, indentStr, false, ctx.forceDefaultStyles, void 0, void 0, ctx.quoteStyle);
|
|
280
286
|
return (allowFold && ctx.lineWidth > 0 ? foldRenderedScalar(rendered, indentStr, ctx.lineWidth) : rendered).split("\n");
|
|
281
287
|
}
|
|
282
288
|
if (Array.isArray(value)) {
|
|
@@ -341,10 +347,11 @@ function stringifyObjectLines(obj, ctx, depth) {
|
|
|
341
347
|
const keys = Object.keys(obj);
|
|
342
348
|
if (keys.length === 0) return ["{}"];
|
|
343
349
|
if (ctx.sortKeys) keys.sort();
|
|
350
|
+
const renderPlainKey = (k) => renderString(k, "plain", "", false, false, void 0, void 0, ctx.quoteStyle);
|
|
344
351
|
if (ctx.defaultCollectionStyle === "flow") return [`{${keys.map((k) => {
|
|
345
|
-
return `${
|
|
352
|
+
return `${renderPlainKey(k)}: ${stringifyLines(obj[k], ctx, depth + 1, false).join(" ")}`;
|
|
346
353
|
}).join(", ")}}`];
|
|
347
|
-
const renderKey = (k) => k.includes("\n") ? renderDoubleQuoted(k, ctx.forceDefaultStyles) :
|
|
354
|
+
const renderKey = (k) => k.includes("\n") ? renderDoubleQuoted(k, ctx.forceDefaultStyles) : renderPlainKey(k);
|
|
348
355
|
const pad = " ".repeat(ctx.indent);
|
|
349
356
|
const lines = [];
|
|
350
357
|
for (const k of keys) {
|
|
@@ -531,12 +538,45 @@ function stringifyScalarNodeLines(node, ctx) {
|
|
|
531
538
|
else lines = ["null"];
|
|
532
539
|
else if (typeof val === "boolean") lines = [val ? "true" : "false"];
|
|
533
540
|
else if (typeof val === "number") lines = [node.raw !== void 0 ? node.raw : renderNumber(val)];
|
|
534
|
-
else if (typeof val === "string") lines = renderString(val, style, " ".repeat(ctx.indent), !!node.tag, ctx.forceDefaultStyles, node.chomp, ctx.parentPosition).split("\n");
|
|
541
|
+
else if (typeof val === "string") lines = renderString(val, style, " ".repeat(ctx.indent), !!node.tag, ctx.forceDefaultStyles, node.chomp, ctx.parentPosition, ctx.quoteStyle).split("\n");
|
|
535
542
|
else lines = [renderDoubleQuoted(String(val))];
|
|
536
543
|
if (node.tag) lines[0] = `${node.tag} ${lines[0]}`;
|
|
537
544
|
if (node.anchor) lines[0] = `&${node.anchor} ${lines[0]}`;
|
|
538
545
|
return lines;
|
|
539
546
|
}
|
|
547
|
+
/** The YAML merge key. */
|
|
548
|
+
const MERGE_KEY = "<<";
|
|
549
|
+
/**
|
|
550
|
+
* True when a mapping key must be emitted plain because quoting it would
|
|
551
|
+
* change what the document means.
|
|
552
|
+
*
|
|
553
|
+
* The merge key is the only scalar in this position where the plain and
|
|
554
|
+
* quoted forms resolve to different YAML types: a plain `<<` key resolves to
|
|
555
|
+
* `tag:yaml.org,2002:merge` and splices the aliased mapping into its parent,
|
|
556
|
+
* while `'<<'` is an ordinary string key that merges nothing. The difference
|
|
557
|
+
* is silent — the document still parses and still round-trips — so re-emitting
|
|
558
|
+
* a merge key quoted is a semantic rewrite of the kind the fidelity obligation
|
|
559
|
+
* forbids.
|
|
560
|
+
*
|
|
561
|
+
* Deliberately narrow. It fires only on a plain-styled scalar carrying no tag
|
|
562
|
+
* and no anchor, so an author's explicitly quoted `'<<'` (a literal string key
|
|
563
|
+
* they meant) and any tagged key keep the form they were written in. A key
|
|
564
|
+
* with no style at all counts as plain: an unstyled `<<` built by hand can
|
|
565
|
+
* only have been meant as a merge key, and preserving that meaning outranks
|
|
566
|
+
* normalizing its presentation.
|
|
567
|
+
*/
|
|
568
|
+
function isPlainMergeKey(node) {
|
|
569
|
+
return node instanceof YamlScalar && node.value === MERGE_KEY && (node.style ?? "plain") === "plain" && node.tag === void 0 && node.anchor === void 0;
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Renders a mapping key, honoring the keys that must stay plain before
|
|
573
|
+
* falling back to ordinary node rendering. Both the flow and the block
|
|
574
|
+
* mapping branches route through here so the two cannot disagree.
|
|
575
|
+
*/
|
|
576
|
+
function stringifyMappingKeyLines(node, ctx, depth) {
|
|
577
|
+
if (isPlainMergeKey(node)) return [MERGE_KEY];
|
|
578
|
+
return stringifyNodeLines(node, ctx, depth);
|
|
579
|
+
}
|
|
540
580
|
/**
|
|
541
581
|
* Stringifies a YamlMap node into lines, using the node's collection style.
|
|
542
582
|
*/
|
|
@@ -556,7 +596,7 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
556
596
|
}
|
|
557
597
|
if (style === "flow") {
|
|
558
598
|
let line = `{${items.map((pair) => {
|
|
559
|
-
return `${pair.key ?
|
|
599
|
+
return `${pair.key ? stringifyMappingKeyLines(pair.key, ctx, depth + 1).join(" ") : "null"}: ${pair.value ? stringifyNodeLines(pair.value, ctx, depth + 1).join(" ") : "null"}`;
|
|
560
600
|
}).join(", ")}}`;
|
|
561
601
|
const flowPrefix = buildMetadataPrefix(node.tag, node.anchor);
|
|
562
602
|
if (flowPrefix) line = `${flowPrefix} ${line}`;
|
|
@@ -603,7 +643,7 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
603
643
|
}
|
|
604
644
|
let resolvedKeyStr;
|
|
605
645
|
if (ctx.forceDefaultStyles && node.style === "flow" && node.sourceMultiline !== true && pair.key instanceof YamlScalar && (pair.key.style === "single-quoted" || pair.key.style === "double-quoted") && typeof pair.key.value === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(pair.key.value)) resolvedKeyStr = pair.key.value;
|
|
606
|
-
else resolvedKeyStr = pair.key ?
|
|
646
|
+
else resolvedKeyStr = pair.key ? stringifyMappingKeyLines(pair.key, ctx, depth + 1).join(" ") : "null";
|
|
607
647
|
const keyStr = resolvedKeyStr;
|
|
608
648
|
const keyIsAnchoredOrTaggedEmpty = pair.key instanceof YamlScalar && pair.key.length === 0 && (pair.key.value === null || pair.key.value === void 0 || pair.key.value === "") && (pair.key.anchor !== void 0 || pair.key.tag !== void 0);
|
|
609
649
|
const sep = pair.key instanceof YamlAlias || keyIsAnchoredOrTaggedEmpty ? " :" : ":";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/yaml",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Zero-dependency YAML parsing, editing and formatting as Effect schemas.",
|
|
6
6
|
"keywords": [
|
|
@@ -34,12 +34,13 @@
|
|
|
34
34
|
"exports": {
|
|
35
35
|
".": {
|
|
36
36
|
"types": "./index.d.ts",
|
|
37
|
-
"import": "./index.js"
|
|
37
|
+
"import": "./index.js",
|
|
38
|
+
"default": "./index.js"
|
|
38
39
|
},
|
|
39
40
|
"./package.json": "./package.json"
|
|
40
41
|
},
|
|
41
42
|
"peerDependencies": {
|
|
42
|
-
"effect": "4.0.0-beta.
|
|
43
|
+
"effect": "4.0.0-beta.99"
|
|
43
44
|
},
|
|
44
45
|
"engines": {
|
|
45
46
|
"node": ">=24.11.0"
|