[d24f17c] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | var resolveBlockScalar = require('../compose/resolve-block-scalar.js');
|
---|
| 4 | var resolveFlowScalar = require('../compose/resolve-flow-scalar.js');
|
---|
| 5 | var errors = require('../errors.js');
|
---|
| 6 | var stringifyString = require('../stringify/stringifyString.js');
|
---|
| 7 |
|
---|
| 8 | function 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(token, strict, _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 | */
|
---|
| 42 | function 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 | */
|
---|
| 90 | function 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 | }
|
---|
| 134 | function 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 */
|
---|
| 160 | function 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 | }
|
---|
| 174 | function 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 |
|
---|
| 216 | exports.createScalarToken = createScalarToken;
|
---|
| 217 | exports.resolveAsScalar = resolveAsScalar;
|
---|
| 218 | exports.setScalarValue = setScalarValue;
|
---|