UNPKG

9.38 kBJavaScriptView Raw
1'use strict';
2
3var resolveBlockScalar = require('../compose/resolve-block-scalar.js');
4var resolveFlowScalar = require('../compose/resolve-flow-scalar.js');
5var errors = require('../errors.js');
6var stringifyString = require('../stringify/stringifyString.js');
7
8function resolveAsScalar(token, strict = true, onError) {
9 if (token) {
10 const _onError = (pos, code, message) => {
11 const offset = typeof pos === 'number' ? pos : Array.isArray(pos) ? pos[0] : pos.offset;
12 if (onError)
13 onError(offset, code, message);
14 else
15 throw new errors.YAMLParseError([offset, offset + 1], code, message);
16 };
17 switch (token.type) {
18 case 'scalar':
19 case 'single-quoted-scalar':
20 case 'double-quoted-scalar':
21 return resolveFlowScalar.resolveFlowScalar(token, strict, _onError);
22 case 'block-scalar':
23 return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError);
24 }
25 }
26 return null;
27}
28/**
29 * Create a new scalar token with `value`
30 *
31 * Values that represent an actual string but may be parsed as a different type should use a `type` other than `'PLAIN'`,
32 * as this function does not support any schema operations and won't check for such conflicts.
33 *
34 * @param value The string representation of the value, which will have its content properly indented.
35 * @param context.end Comments and whitespace after the end of the value, or after the block scalar header. If undefined, a newline will be added.
36 * @param context.implicitKey Being within an implicit key may affect the resolved type of the token's value.
37 * @param context.indent The indent level of the token.
38 * @param context.inFlow Is this scalar within a flow collection? This may affect the resolved type of the token's value.
39 * @param context.offset The offset position of the token.
40 * @param context.type The preferred type of the scalar token. If undefined, the previous type of the `token` will be used, defaulting to `'PLAIN'`.
41 */
42function createScalarToken(value, context) {
43 const { implicitKey = false, indent, inFlow = false, offset = -1, type = 'PLAIN' } = context;
44 const source = stringifyString.stringifyString({ type, value }, {
45 implicitKey,
46 indent: indent > 0 ? ' '.repeat(indent) : '',
47 inFlow,
48 options: { blockQuote: true, lineWidth: -1 }
49 });
50 const end = context.end ?? [
51 { type: 'newline', offset: -1, indent, source: '\n' }
52 ];
53 switch (source[0]) {
54 case '|':
55 case '>': {
56 const he = source.indexOf('\n');
57 const head = source.substring(0, he);
58 const body = source.substring(he + 1) + '\n';
59 const props = [
60 { type: 'block-scalar-header', offset, indent, source: head }
61 ];
62 if (!addEndtoBlockProps(props, end))
63 props.push({ type: 'newline', offset: -1, indent, source: '\n' });
64 return { type: 'block-scalar', offset, indent, props, source: body };
65 }
66 case '"':
67 return { type: 'double-quoted-scalar', offset, indent, source, end };
68 case "'":
69 return { type: 'single-quoted-scalar', offset, indent, source, end };
70 default:
71 return { type: 'scalar', offset, indent, source, end };
72 }
73}
74/**
75 * Set the value of `token` to the given string `value`, overwriting any previous contents and type that it may have.
76 *
77 * Best efforts are made to retain any comments previously associated with the `token`,
78 * though all contents within a collection's `items` will be overwritten.
79 *
80 * Values that represent an actual string but may be parsed as a different type should use a `type` other than `'PLAIN'`,
81 * as this function does not support any schema operations and won't check for such conflicts.
82 *
83 * @param token Any token. If it does not include an `indent` value, the value will be stringified as if it were an implicit key.
84 * @param value The string representation of the value, which will have its content properly indented.
85 * @param context.afterKey In most cases, values after a key should have an additional level of indentation.
86 * @param context.implicitKey Being within an implicit key may affect the resolved type of the token's value.
87 * @param context.inFlow Being within a flow collection may affect the resolved type of the token's value.
88 * @param context.type The preferred type of the scalar token. If undefined, the previous type of the `token` will be used, defaulting to `'PLAIN'`.
89 */
90function setScalarValue(token, value, context = {}) {
91 let { afterKey = false, implicitKey = false, inFlow = false, type } = context;
92 let indent = 'indent' in token ? token.indent : null;
93 if (afterKey && typeof indent === 'number')
94 indent += 2;
95 if (!type)
96 switch (token.type) {
97 case 'single-quoted-scalar':
98 type = 'QUOTE_SINGLE';
99 break;
100 case 'double-quoted-scalar':
101 type = 'QUOTE_DOUBLE';
102 break;
103 case 'block-scalar': {
104 const header = token.props[0];
105 if (header.type !== 'block-scalar-header')
106 throw new Error('Invalid block scalar header');
107 type = header.source[0] === '>' ? 'BLOCK_FOLDED' : 'BLOCK_LITERAL';
108 break;
109 }
110 default:
111 type = 'PLAIN';
112 }
113 const source = stringifyString.stringifyString({ type, value }, {
114 implicitKey: implicitKey || indent === null,
115 indent: indent !== null && indent > 0 ? ' '.repeat(indent) : '',
116 inFlow,
117 options: { blockQuote: true, lineWidth: -1 }
118 });
119 switch (source[0]) {
120 case '|':
121 case '>':
122 setBlockScalarValue(token, source);
123 break;
124 case '"':
125 setFlowScalarValue(token, source, 'double-quoted-scalar');
126 break;
127 case "'":
128 setFlowScalarValue(token, source, 'single-quoted-scalar');
129 break;
130 default:
131 setFlowScalarValue(token, source, 'scalar');
132 }
133}
134function setBlockScalarValue(token, source) {
135 const he = source.indexOf('\n');
136 const head = source.substring(0, he);
137 const body = source.substring(he + 1) + '\n';
138 if (token.type === 'block-scalar') {
139 const header = token.props[0];
140 if (header.type !== 'block-scalar-header')
141 throw new Error('Invalid block scalar header');
142 header.source = head;
143 token.source = body;
144 }
145 else {
146 const { offset } = token;
147 const indent = 'indent' in token ? token.indent : -1;
148 const props = [
149 { type: 'block-scalar-header', offset, indent, source: head }
150 ];
151 if (!addEndtoBlockProps(props, 'end' in token ? token.end : undefined))
152 props.push({ type: 'newline', offset: -1, indent, source: '\n' });
153 for (const key of Object.keys(token))
154 if (key !== 'type' && key !== 'offset')
155 delete token[key];
156 Object.assign(token, { type: 'block-scalar', indent, props, source: body });
157 }
158}
159/** @returns `true` if last token is a newline */
160function addEndtoBlockProps(props, end) {
161 if (end)
162 for (const st of end)
163 switch (st.type) {
164 case 'space':
165 case 'comment':
166 props.push(st);
167 break;
168 case 'newline':
169 props.push(st);
170 return true;
171 }
172 return false;
173}
174function setFlowScalarValue(token, source, type) {
175 switch (token.type) {
176 case 'scalar':
177 case 'double-quoted-scalar':
178 case 'single-quoted-scalar':
179 token.type = type;
180 token.source = source;
181 break;
182 case 'block-scalar': {
183 const end = token.props.slice(1);
184 let oa = source.length;
185 if (token.props[0].type === 'block-scalar-header')
186 oa -= token.props[0].source.length;
187 for (const tok of end)
188 tok.offset += oa;
189 delete token.props;
190 Object.assign(token, { type, source, end });
191 break;
192 }
193 case 'block-map':
194 case 'block-seq': {
195 const offset = token.offset + source.length;
196 const nl = { type: 'newline', offset, indent: token.indent, source: '\n' };
197 delete token.items;
198 Object.assign(token, { type, source, end: [nl] });
199 break;
200 }
201 default: {
202 const indent = 'indent' in token ? token.indent : -1;
203 const end = 'end' in token && Array.isArray(token.end)
204 ? token.end.filter(st => st.type === 'space' ||
205 st.type === 'comment' ||
206 st.type === 'newline')
207 : [];
208 for (const key of Object.keys(token))
209 if (key !== 'type' && key !== 'offset')
210 delete token[key];
211 Object.assign(token, { type, indent, source, end });
212 }
213 }
214}
215
216exports.createScalarToken = createScalarToken;
217exports.resolveAsScalar = resolveAsScalar;
218exports.setScalarValue = setScalarValue;