123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- import { resolveBlockScalar } from '../compose/resolve-block-scalar.js';
- import { resolveFlowScalar } from '../compose/resolve-flow-scalar.js';
- import { YAMLParseError } from '../errors.js';
- import { stringifyString } from '../stringify/stringifyString.js';
- function resolveAsScalar(token, strict = true, onError) {
- if (token) {
- const _onError = (pos, code, message) => {
- const offset = typeof pos === 'number' ? pos : Array.isArray(pos) ? pos[0] : pos.offset;
- if (onError)
- onError(offset, code, message);
- else
- throw new YAMLParseError([offset, offset + 1], code, message);
- };
- switch (token.type) {
- case 'scalar':
- case 'single-quoted-scalar':
- case 'double-quoted-scalar':
- return resolveFlowScalar(token, strict, _onError);
- case 'block-scalar':
- return resolveBlockScalar({ options: { strict } }, token, _onError);
- }
- }
- return null;
- }
- /**
- * Create a new scalar token with `value`
- *
- * Values that represent an actual string but may be parsed as a different type should use a `type` other than `'PLAIN'`,
- * as this function does not support any schema operations and won't check for such conflicts.
- *
- * @param value The string representation of the value, which will have its content properly indented.
- * @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.
- * @param context.implicitKey Being within an implicit key may affect the resolved type of the token's value.
- * @param context.indent The indent level of the token.
- * @param context.inFlow Is this scalar within a flow collection? This may affect the resolved type of the token's value.
- * @param context.offset The offset position of the token.
- * @param context.type The preferred type of the scalar token. If undefined, the previous type of the `token` will be used, defaulting to `'PLAIN'`.
- */
- function createScalarToken(value, context) {
- const { implicitKey = false, indent, inFlow = false, offset = -1, type = 'PLAIN' } = context;
- const source = stringifyString({ type, value }, {
- implicitKey,
- indent: indent > 0 ? ' '.repeat(indent) : '',
- inFlow,
- options: { blockQuote: true, lineWidth: -1 }
- });
- const end = context.end ?? [
- { type: 'newline', offset: -1, indent, source: '\n' }
- ];
- switch (source[0]) {
- case '|':
- case '>': {
- const he = source.indexOf('\n');
- const head = source.substring(0, he);
- const body = source.substring(he + 1) + '\n';
- const props = [
- { type: 'block-scalar-header', offset, indent, source: head }
- ];
- if (!addEndtoBlockProps(props, end))
- props.push({ type: 'newline', offset: -1, indent, source: '\n' });
- return { type: 'block-scalar', offset, indent, props, source: body };
- }
- case '"':
- return { type: 'double-quoted-scalar', offset, indent, source, end };
- case "'":
- return { type: 'single-quoted-scalar', offset, indent, source, end };
- default:
- return { type: 'scalar', offset, indent, source, end };
- }
- }
- /**
- * Set the value of `token` to the given string `value`, overwriting any previous contents and type that it may have.
- *
- * Best efforts are made to retain any comments previously associated with the `token`,
- * though all contents within a collection's `items` will be overwritten.
- *
- * Values that represent an actual string but may be parsed as a different type should use a `type` other than `'PLAIN'`,
- * as this function does not support any schema operations and won't check for such conflicts.
- *
- * @param token Any token. If it does not include an `indent` value, the value will be stringified as if it were an implicit key.
- * @param value The string representation of the value, which will have its content properly indented.
- * @param context.afterKey In most cases, values after a key should have an additional level of indentation.
- * @param context.implicitKey Being within an implicit key may affect the resolved type of the token's value.
- * @param context.inFlow Being within a flow collection may affect the resolved type of the token's value.
- * @param context.type The preferred type of the scalar token. If undefined, the previous type of the `token` will be used, defaulting to `'PLAIN'`.
- */
- function setScalarValue(token, value, context = {}) {
- let { afterKey = false, implicitKey = false, inFlow = false, type } = context;
- let indent = 'indent' in token ? token.indent : null;
- if (afterKey && typeof indent === 'number')
- indent += 2;
- if (!type)
- switch (token.type) {
- case 'single-quoted-scalar':
- type = 'QUOTE_SINGLE';
- break;
- case 'double-quoted-scalar':
- type = 'QUOTE_DOUBLE';
- break;
- case 'block-scalar': {
- const header = token.props[0];
- if (header.type !== 'block-scalar-header')
- throw new Error('Invalid block scalar header');
- type = header.source[0] === '>' ? 'BLOCK_FOLDED' : 'BLOCK_LITERAL';
- break;
- }
- default:
- type = 'PLAIN';
- }
- const source = stringifyString({ type, value }, {
- implicitKey: implicitKey || indent === null,
- indent: indent !== null && indent > 0 ? ' '.repeat(indent) : '',
- inFlow,
- options: { blockQuote: true, lineWidth: -1 }
- });
- switch (source[0]) {
- case '|':
- case '>':
- setBlockScalarValue(token, source);
- break;
- case '"':
- setFlowScalarValue(token, source, 'double-quoted-scalar');
- break;
- case "'":
- setFlowScalarValue(token, source, 'single-quoted-scalar');
- break;
- default:
- setFlowScalarValue(token, source, 'scalar');
- }
- }
- function setBlockScalarValue(token, source) {
- const he = source.indexOf('\n');
- const head = source.substring(0, he);
- const body = source.substring(he + 1) + '\n';
- if (token.type === 'block-scalar') {
- const header = token.props[0];
- if (header.type !== 'block-scalar-header')
- throw new Error('Invalid block scalar header');
- header.source = head;
- token.source = body;
- }
- else {
- const { offset } = token;
- const indent = 'indent' in token ? token.indent : -1;
- const props = [
- { type: 'block-scalar-header', offset, indent, source: head }
- ];
- if (!addEndtoBlockProps(props, 'end' in token ? token.end : undefined))
- props.push({ type: 'newline', offset: -1, indent, source: '\n' });
- for (const key of Object.keys(token))
- if (key !== 'type' && key !== 'offset')
- delete token[key];
- Object.assign(token, { type: 'block-scalar', indent, props, source: body });
- }
- }
- /** @returns `true` if last token is a newline */
- function addEndtoBlockProps(props, end) {
- if (end)
- for (const st of end)
- switch (st.type) {
- case 'space':
- case 'comment':
- props.push(st);
- break;
- case 'newline':
- props.push(st);
- return true;
- }
- return false;
- }
- function setFlowScalarValue(token, source, type) {
- switch (token.type) {
- case 'scalar':
- case 'double-quoted-scalar':
- case 'single-quoted-scalar':
- token.type = type;
- token.source = source;
- break;
- case 'block-scalar': {
- const end = token.props.slice(1);
- let oa = source.length;
- if (token.props[0].type === 'block-scalar-header')
- oa -= token.props[0].source.length;
- for (const tok of end)
- tok.offset += oa;
- delete token.props;
- Object.assign(token, { type, source, end });
- break;
- }
- case 'block-map':
- case 'block-seq': {
- const offset = token.offset + source.length;
- const nl = { type: 'newline', offset, indent: token.indent, source: '\n' };
- delete token.items;
- Object.assign(token, { type, source, end: [nl] });
- break;
- }
- default: {
- const indent = 'indent' in token ? token.indent : -1;
- const end = 'end' in token && Array.isArray(token.end)
- ? token.end.filter(st => st.type === 'space' ||
- st.type === 'comment' ||
- st.type === 'newline')
- : [];
- for (const key of Object.keys(token))
- if (key !== 'type' && key !== 'offset')
- delete token[key];
- Object.assign(token, { type, indent, source, end });
- }
- }
- }
- export { createScalarToken, resolveAsScalar, setScalarValue };
|