yaml 2.3.4 → 2.4.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 CHANGED
@@ -33,6 +33,8 @@ npm install yaml
33
33
  The API provided by `yaml` has three layers, depending on how deep you need to go: [Parse & Stringify](https://eemeli.org/yaml/#parse-amp-stringify), [Documents](https://eemeli.org/yaml/#documents), and the underlying [Lexer/Parser/Composer](https://eemeli.org/yaml/#parsing-yaml).
34
34
  The first has the simplest API and "just works", the second gets you all the bells and whistles supported by the library along with a decent [AST](https://eemeli.org/yaml/#content-nodes), and the third lets you get progressively closer to YAML source, if that's your thing.
35
35
 
36
+ A [command-line tool](https://eemeli.org/yaml/#command-line-tool) is also included.
37
+
36
38
  ```js
37
39
  import { parse, stringify } from 'yaml'
38
40
  // or
@@ -55,26 +57,26 @@ const YAML = require('yaml')
55
57
  - [`#directives`](https://eemeli.org/yaml/#stream-directives)
56
58
  - [`#errors`](https://eemeli.org/yaml/#errors)
57
59
  - [`#warnings`](https://eemeli.org/yaml/#errors)
58
- - [`isDocument(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
60
+ - [`isDocument(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
59
61
  - [`parseAllDocuments(str, options?): Document[]`](https://eemeli.org/yaml/#parsing-documents)
60
62
  - [`parseDocument(str, options?): Document`](https://eemeli.org/yaml/#parsing-documents)
61
63
 
62
64
  ### Content Nodes
63
65
 
64
- - [`isAlias(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
65
- - [`isCollection(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
66
- - [`isMap(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
67
- - [`isNode(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
68
- - [`isPair(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
69
- - [`isScalar(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
70
- - [`isSeq(foo): boolean`](https://eemeli.org/yaml/#identifying-nodes)
66
+ - [`isAlias(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
67
+ - [`isCollection(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
68
+ - [`isMap(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
69
+ - [`isNode(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
70
+ - [`isPair(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
71
+ - [`isScalar(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
72
+ - [`isSeq(foo): boolean`](https://eemeli.org/yaml/#identifying-node-types)
71
73
  - [`new Scalar(value)`](https://eemeli.org/yaml/#scalar-values)
72
74
  - [`new YAMLMap()`](https://eemeli.org/yaml/#collections)
73
75
  - [`new YAMLSeq()`](https://eemeli.org/yaml/#collections)
74
76
  - [`doc.createAlias(node, name?): Alias`](https://eemeli.org/yaml/#working-with-anchors)
75
77
  - [`doc.createNode(value, options?): Node`](https://eemeli.org/yaml/#creating-nodes)
76
78
  - [`doc.createPair(key, value): Pair`](https://eemeli.org/yaml/#creating-nodes)
77
- - [`visit(node, visitor)`](https://eemeli.org/yaml/#modifying-nodes)
79
+ - [`visit(node, visitor)`](https://eemeli.org/yaml/#finding-and-modifying-nodes)
78
80
 
79
81
  ### Parsing YAML
80
82
 
package/bin.mjs ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { UserError, cli, help } from './dist/cli.mjs'
4
+
5
+ cli(process.stdin, error => {
6
+ if (error instanceof UserError) {
7
+ if (error.code === UserError.ARGS) console.error(`${help}\n`)
8
+ console.error(error.message)
9
+ process.exitCode = error.code
10
+ } else if (error) throw error
11
+ })
@@ -189,19 +189,19 @@ function foldNewline(source, offset) {
189
189
  return { fold, offset };
190
190
  }
191
191
  const escapeCodes = {
192
- '0': '\0',
193
- a: '\x07',
194
- b: '\b',
195
- e: '\x1b',
196
- f: '\f',
197
- n: '\n',
198
- r: '\r',
199
- t: '\t',
200
- v: '\v',
201
- N: '\u0085',
202
- _: '\u00a0',
203
- L: '\u2028',
204
- P: '\u2029',
192
+ '0': '\0', // null character
193
+ a: '\x07', // bell character
194
+ b: '\b', // backspace
195
+ e: '\x1b', // escape character
196
+ f: '\f', // form feed
197
+ n: '\n', // line feed
198
+ r: '\r', // carriage return
199
+ t: '\t', // horizontal tab
200
+ v: '\v', // vertical tab
201
+ N: '\u0085', // Unicode next line
202
+ _: '\u00a0', // Unicode non-breaking space
203
+ L: '\u2028', // Unicode line separator
204
+ P: '\u2029', // Unicode paragraph separator
205
205
  ' ': ' ',
206
206
  '"': '"',
207
207
  '/': '/',
@@ -2,7 +2,7 @@ import { Scalar } from '../../nodes/Scalar.js';
2
2
  import { stringifyString } from '../../stringify/stringifyString.js';
3
3
 
4
4
  const binary = {
5
- identify: value => value instanceof Uint8Array,
5
+ identify: value => value instanceof Uint8Array, // Buffer inherits from Uint8Array
6
6
  default: false,
7
7
  tag: 'tag:yaml.org,2002:binary',
8
8
  /**
@@ -1,4 +1,3 @@
1
- import { Collection } from '../nodes/Collection.js';
2
1
  import { isNode, isPair } from '../nodes/identity.js';
3
2
  import { stringify } from './stringify.js';
4
3
  import { lineComment, indentComment } from './stringifyComment.js';
@@ -120,7 +119,7 @@ function stringifyFlowCollection({ comment, items }, ctx, { flowChars, itemInden
120
119
  else {
121
120
  if (!reqNewline) {
122
121
  const len = lines.reduce((sum, line) => sum + line.length + 2, 2);
123
- reqNewline = len > Collection.maxFlowStringSingleLineLength;
122
+ reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;
124
123
  }
125
124
  if (reqNewline) {
126
125
  str = start;
package/dist/cli.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /// <reference types="node" />
2
+ export declare const help = "yaml: A command-line YAML processor and inspector\n\nReads stdin and writes output to stdout and errors & warnings to stderr.\n\nUsage:\n yaml Process a YAML stream, outputting it as YAML\n yaml cst Parse the CST of a YAML stream\n yaml lex Parse the lexical tokens of a YAML stream\n yaml valid Validate a YAML stream, returning 0 on success\n\nOptions:\n --help, -h Show this message.\n --json, -j Output JSON.\n\nAdditional options for bare \"yaml\" command:\n --doc, -d Output pretty-printed JS Document objects.\n --single, -1 Require the input to consist of a single YAML document.\n --strict, -s Stop on errors.\n --visit, -v Apply a visitor to each document (requires a path to import)\n --yaml 1.1 Set the YAML version. (default: 1.2)";
3
+ export declare class UserError extends Error {
4
+ static ARGS: number;
5
+ static SINGLE: number;
6
+ code: number;
7
+ constructor(code: number, message: string);
8
+ }
9
+ export declare function cli(stdin: NodeJS.ReadableStream, done: (error?: Error) => void, argv?: string[]): Promise<void>;
package/dist/cli.mjs ADDED
@@ -0,0 +1,195 @@
1
+ import { resolve } from 'node:path';
2
+ import { parseArgs } from 'node:util';
3
+ import { prettyToken } from './parse/cst.js';
4
+ import { Lexer } from './parse/lexer.js';
5
+ import { Parser } from './parse/parser.js';
6
+ import { Composer } from './compose/composer.js';
7
+ import { LineCounter } from './parse/line-counter.js';
8
+ import { prettifyError } from './errors.js';
9
+ import { visit } from './visit.js';
10
+
11
+ const help = `\
12
+ yaml: A command-line YAML processor and inspector
13
+
14
+ Reads stdin and writes output to stdout and errors & warnings to stderr.
15
+
16
+ Usage:
17
+ yaml Process a YAML stream, outputting it as YAML
18
+ yaml cst Parse the CST of a YAML stream
19
+ yaml lex Parse the lexical tokens of a YAML stream
20
+ yaml valid Validate a YAML stream, returning 0 on success
21
+
22
+ Options:
23
+ --help, -h Show this message.
24
+ --json, -j Output JSON.
25
+
26
+ Additional options for bare "yaml" command:
27
+ --doc, -d Output pretty-printed JS Document objects.
28
+ --single, -1 Require the input to consist of a single YAML document.
29
+ --strict, -s Stop on errors.
30
+ --visit, -v Apply a visitor to each document (requires a path to import)
31
+ --yaml 1.1 Set the YAML version. (default: 1.2)`;
32
+ class UserError extends Error {
33
+ constructor(code, message) {
34
+ super(`Error: ${message}`);
35
+ this.code = code;
36
+ }
37
+ }
38
+ UserError.ARGS = 2;
39
+ UserError.SINGLE = 3;
40
+ async function cli(stdin, done, argv) {
41
+ let args;
42
+ try {
43
+ args = parseArgs({
44
+ args: argv,
45
+ allowPositionals: true,
46
+ options: {
47
+ doc: { type: 'boolean', short: 'd' },
48
+ help: { type: 'boolean', short: 'h' },
49
+ json: { type: 'boolean', short: 'j' },
50
+ single: { type: 'boolean', short: '1' },
51
+ strict: { type: 'boolean', short: 's' },
52
+ visit: { type: 'string', short: 'v' },
53
+ yaml: { type: 'string', default: '1.2' }
54
+ }
55
+ });
56
+ }
57
+ catch (error) {
58
+ return done(new UserError(UserError.ARGS, error.message));
59
+ }
60
+ const { positionals: [mode], values: opt } = args;
61
+ stdin.setEncoding('utf-8');
62
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
63
+ switch (opt.help || mode) {
64
+ /* istanbul ignore next */
65
+ case true: // --help
66
+ console.log(help);
67
+ break;
68
+ case 'lex': {
69
+ const lexer = new Lexer();
70
+ const data = [];
71
+ const add = (tok) => {
72
+ if (opt.json)
73
+ data.push(tok);
74
+ else
75
+ console.log(prettyToken(tok));
76
+ };
77
+ stdin.on('data', (chunk) => {
78
+ for (const tok of lexer.lex(chunk, true))
79
+ add(tok);
80
+ });
81
+ stdin.on('end', () => {
82
+ for (const tok of lexer.lex('', false))
83
+ add(tok);
84
+ if (opt.json)
85
+ console.log(JSON.stringify(data));
86
+ done();
87
+ });
88
+ break;
89
+ }
90
+ case 'cst': {
91
+ const parser = new Parser();
92
+ const data = [];
93
+ const add = (tok) => {
94
+ if (opt.json)
95
+ data.push(tok);
96
+ else
97
+ console.dir(tok, { depth: null });
98
+ };
99
+ stdin.on('data', (chunk) => {
100
+ for (const tok of parser.parse(chunk, true))
101
+ add(tok);
102
+ });
103
+ stdin.on('end', () => {
104
+ for (const tok of parser.parse('', false))
105
+ add(tok);
106
+ if (opt.json)
107
+ console.log(JSON.stringify(data));
108
+ done();
109
+ });
110
+ break;
111
+ }
112
+ case undefined:
113
+ case 'valid': {
114
+ const lineCounter = new LineCounter();
115
+ const parser = new Parser(lineCounter.addNewLine);
116
+ // @ts-expect-error Version is validated at runtime
117
+ const composer = new Composer({ version: opt.yaml });
118
+ const visitor = opt.visit
119
+ ? (await import(resolve(opt.visit))).default
120
+ : null;
121
+ let source = '';
122
+ let hasDoc = false;
123
+ let reqDocEnd = false;
124
+ const data = [];
125
+ const add = (doc) => {
126
+ if (hasDoc && opt.single) {
127
+ return done(new UserError(UserError.SINGLE, 'Input stream contains multiple documents'));
128
+ }
129
+ for (const error of doc.errors) {
130
+ prettifyError(source, lineCounter)(error);
131
+ if (opt.strict || mode === 'valid')
132
+ return done(error);
133
+ console.error(error);
134
+ }
135
+ for (const warning of doc.warnings) {
136
+ prettifyError(source, lineCounter)(warning);
137
+ console.error(warning);
138
+ }
139
+ if (visitor)
140
+ visit(doc, visitor);
141
+ if (mode === 'valid')
142
+ doc.toJS();
143
+ else if (opt.json)
144
+ data.push(doc);
145
+ else if (opt.doc) {
146
+ Object.defineProperties(doc, {
147
+ options: { enumerable: false },
148
+ schema: { enumerable: false }
149
+ });
150
+ console.dir(doc, { depth: null });
151
+ }
152
+ else {
153
+ if (reqDocEnd)
154
+ console.log('...');
155
+ try {
156
+ const str = String(doc);
157
+ console.log(str.endsWith('\n') ? str.slice(0, -1) : str);
158
+ }
159
+ catch (error) {
160
+ done(error);
161
+ }
162
+ }
163
+ hasDoc = true;
164
+ reqDocEnd = !doc.directives?.docEnd;
165
+ };
166
+ stdin.on('data', (chunk) => {
167
+ source += chunk;
168
+ for (const tok of parser.parse(chunk, true)) {
169
+ for (const doc of composer.next(tok))
170
+ add(doc);
171
+ }
172
+ });
173
+ stdin.on('end', () => {
174
+ for (const tok of parser.parse('', false)) {
175
+ for (const doc of composer.next(tok))
176
+ add(doc);
177
+ }
178
+ for (const doc of composer.end(false))
179
+ add(doc);
180
+ if (opt.single && !hasDoc) {
181
+ return done(new UserError(UserError.SINGLE, 'Input stream contained no documents'));
182
+ }
183
+ if (mode !== 'valid' && opt.json) {
184
+ console.log(JSON.stringify(opt.single ? data[0] : data));
185
+ }
186
+ done();
187
+ });
188
+ break;
189
+ }
190
+ default:
191
+ done(new UserError(UserError.ARGS, `Unknown command: ${JSON.stringify(mode)}`));
192
+ }
193
+ }
194
+
195
+ export { UserError, cli, help };
@@ -191,19 +191,19 @@ function foldNewline(source, offset) {
191
191
  return { fold, offset };
192
192
  }
193
193
  const escapeCodes = {
194
- '0': '\0',
195
- a: '\x07',
196
- b: '\b',
197
- e: '\x1b',
198
- f: '\f',
199
- n: '\n',
200
- r: '\r',
201
- t: '\t',
202
- v: '\v',
203
- N: '\u0085',
204
- _: '\u00a0',
205
- L: '\u2028',
206
- P: '\u2029',
194
+ '0': '\0', // null character
195
+ a: '\x07', // bell character
196
+ b: '\b', // backspace
197
+ e: '\x1b', // escape character
198
+ f: '\f', // form feed
199
+ n: '\n', // line feed
200
+ r: '\r', // carriage return
201
+ t: '\t', // horizontal tab
202
+ v: '\v', // vertical tab
203
+ N: '\u0085', // Unicode next line
204
+ _: '\u00a0', // Unicode non-breaking space
205
+ L: '\u2028', // Unicode line separator
206
+ P: '\u2029', // Unicode paragraph separator
207
207
  ' ': ' ',
208
208
  '"': '"',
209
209
  '/': '/',
@@ -5,7 +5,7 @@ import type { StringifyContext } from '../stringify/stringify.js';
5
5
  import { addPairToJSMap } from './addPairToJSMap.js';
6
6
  import { NODE_TYPE } from './identity.js';
7
7
  import type { ToJSContext } from './toJS.js';
8
- export declare function createPair(key: unknown, value: unknown, ctx: CreateNodeContext): Pair<import("./Node.js").Node, import("./YAMLMap.js").YAMLMap<unknown, unknown> | import("./Scalar.js").Scalar<unknown> | import("./Alias.js").Alias | import("./YAMLSeq.js").YAMLSeq<unknown>>;
8
+ export declare function createPair(key: unknown, value: unknown, ctx: CreateNodeContext): Pair<import("./Node.js").Node, import("./Alias.js").Alias | import("./Scalar.js").Scalar<unknown> | import("./YAMLMap.js").YAMLMap<unknown, unknown> | import("./YAMLSeq.js").YAMLSeq<unknown>>;
9
9
  export declare class Pair<K = unknown, V = unknown> {
10
10
  readonly [NODE_TYPE]: symbol;
11
11
  /** Always Node or null when parsed, but can be set to anything. */
@@ -99,7 +99,7 @@ export declare const SCALAR = "\u001F";
99
99
  /** @returns `true` if `token` is a flow or block collection */
100
100
  export declare const isCollection: (token: Token | null | undefined) => token is BlockMap | BlockSequence | FlowCollection;
101
101
  /** @returns `true` if `token` is a flow or block scalar; not an alias */
102
- export declare const isScalar: (token: Token | null | undefined) => token is BlockScalar | FlowScalar;
102
+ export declare const isScalar: (token: Token | null | undefined) => token is FlowScalar | BlockScalar;
103
103
  /** Get a printable representation of a lexer token */
104
104
  export declare function prettyToken(token: string): string;
105
105
  /** Identify the type of a lexer token. May return `null` for unknown tokens. */
@@ -4,7 +4,7 @@ var Scalar = require('../../nodes/Scalar.js');
4
4
  var stringifyString = require('../../stringify/stringifyString.js');
5
5
 
6
6
  const binary = {
7
- identify: value => value instanceof Uint8Array,
7
+ identify: value => value instanceof Uint8Array, // Buffer inherits from Uint8Array
8
8
  default: false,
9
9
  tag: 'tag:yaml.org,2002:binary',
10
10
  /**
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var Collection = require('../nodes/Collection.js');
4
3
  var identity = require('../nodes/identity.js');
5
4
  var stringify = require('./stringify.js');
6
5
  var stringifyComment = require('./stringifyComment.js');
@@ -122,7 +121,7 @@ function stringifyFlowCollection({ comment, items }, ctx, { flowChars, itemInden
122
121
  else {
123
122
  if (!reqNewline) {
124
123
  const len = lines.reduce((sum, line) => sum + line.length + 2, 2);
125
- reqNewline = len > Collection.Collection.maxFlowStringSingleLineLength;
124
+ reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;
126
125
  }
127
126
  if (reqNewline) {
128
127
  str = start;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml",
3
- "version": "2.3.4",
3
+ "version": "2.4.0",
4
4
  "license": "ISC",
5
5
  "author": "Eemeli Aro <eemeli@gmail.com>",
6
6
  "repository": "github:eemeli/yaml",
@@ -18,6 +18,7 @@
18
18
  ],
19
19
  "type": "commonjs",
20
20
  "main": "./dist/index.js",
21
+ "bin": "./bin.mjs",
21
22
  "browser": {
22
23
  "./dist/index.js": "./browser/index.js",
23
24
  "./dist/util.js": "./browser/dist/util.js",
@@ -52,7 +53,9 @@
52
53
  "test:dist:types": "tsc --allowJs --moduleResolution node --noEmit --target es5 dist/index.js",
53
54
  "test:types": "tsc --noEmit && tsc --noEmit -p tests/tsconfig.json",
54
55
  "docs:install": "cd docs-slate && bundle install",
56
+ "predocs:deploy": "node docs/prepare-docs.mjs",
55
57
  "docs:deploy": "cd docs-slate && ./deploy.sh",
58
+ "predocs": "node docs/prepare-docs.mjs",
56
59
  "docs": "cd docs-slate && bundle exec middleman server",
57
60
  "preversion": "npm test && npm run build",
58
61
  "prepublishOnly": "npm run clean && npm test && npm run build"
@@ -66,17 +69,17 @@
66
69
  },
67
70
  "devDependencies": {
68
71
  "@babel/core": "^7.12.10",
69
- "@babel/plugin-proposal-class-properties": "^7.12.1",
70
- "@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
72
+ "@babel/plugin-transform-class-properties": "^7.23.3",
73
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4",
71
74
  "@babel/plugin-transform-typescript": "^7.12.17",
72
75
  "@babel/preset-env": "^7.12.11",
73
76
  "@rollup/plugin-babel": "^6.0.3",
74
77
  "@rollup/plugin-replace": "^5.0.2",
75
78
  "@rollup/plugin-typescript": "^11.0.0",
76
79
  "@types/jest": "^29.2.4",
77
- "@types/node": "^14.18.35",
78
- "@typescript-eslint/eslint-plugin": "^6.4.1",
79
- "@typescript-eslint/parser": "^6.4.1",
80
+ "@types/node": "^20.11.20",
81
+ "@typescript-eslint/eslint-plugin": "^7.0.2",
82
+ "@typescript-eslint/parser": "^7.0.2",
80
83
  "babel-jest": "^29.0.1",
81
84
  "cross-env": "^7.0.3",
82
85
  "eslint": "^8.2.0",
@@ -85,7 +88,7 @@
85
88
  "jest": "^29.0.1",
86
89
  "jest-ts-webcompat-resolver": "^1.0.0",
87
90
  "prettier": "^3.0.2",
88
- "rollup": "^3.7.5",
91
+ "rollup": "^4.12.0",
89
92
  "tslib": "^2.1.0",
90
93
  "typescript": "^5.0.3"
91
94
  },