[d24f17c] | 1 | import { tail, compose, pathOr, map, concat, transduce, pipe, trim, split, join, curry } from 'ramda';
|
---|
| 2 | import { isInteger, trimStart, trimEnd, isUndefined, trimCharsStart, isEmptyString, repeatStr, concatRight } from 'ramda-adjunct';
|
---|
| 3 | import { unraw } from 'unraw';
|
---|
| 4 |
|
---|
| 5 | /**
|
---|
| 6 | * Helpers.
|
---|
| 7 | */
|
---|
| 8 |
|
---|
| 9 | const blockStyleRegExp = /^(?<style>[|>])(?<chomping>[+-]?)(?<indentation>[0-9]*)\s/;
|
---|
| 10 | const getIndentationIndicator = content => {
|
---|
| 11 | const matches = content.match(blockStyleRegExp);
|
---|
| 12 | const indicator = pathOr('', ['groups', 'indentation'], matches);
|
---|
| 13 | return isEmptyString(indicator) ? undefined : parseInt(indicator, 10);
|
---|
| 14 | };
|
---|
| 15 | const getIndentation = content => {
|
---|
| 16 | const explicitIndentationIndicator = getIndentationIndicator(content);
|
---|
| 17 |
|
---|
| 18 | // we have explicit indentation indicator
|
---|
| 19 | if (isInteger(explicitIndentationIndicator)) {
|
---|
| 20 | return repeatStr(' ', explicitIndentationIndicator);
|
---|
| 21 | }
|
---|
| 22 |
|
---|
| 23 | // we assume indentation indicator from first line
|
---|
| 24 | const firstLine = pathOr('', [1], content.split('\n'));
|
---|
| 25 | const implicitIndentationIndicator = pathOr(0, ['groups', 'indentation', 'length'], firstLine.match(/^(?<indentation>[ ]*)/));
|
---|
| 26 | return repeatStr(' ', implicitIndentationIndicator);
|
---|
| 27 | };
|
---|
| 28 | const getChompingIndicator = content => {
|
---|
| 29 | const matches = content.match(blockStyleRegExp);
|
---|
| 30 | const indicator = pathOr('', ['groups', 'chomping'], matches);
|
---|
| 31 | return isEmptyString(indicator) ? undefined : indicator;
|
---|
| 32 | };
|
---|
| 33 | const chomp = (indicator, content) => {
|
---|
| 34 | // clip (single newline at end)
|
---|
| 35 | if (isUndefined(indicator)) {
|
---|
| 36 | return `${trimEnd(content)}\n`;
|
---|
| 37 | }
|
---|
| 38 | // strip (no newline at end)
|
---|
| 39 | if (indicator === '-') {
|
---|
| 40 | return trimEnd(content);
|
---|
| 41 | }
|
---|
| 42 | // keep (all newlines from end)
|
---|
| 43 | if (indicator === '+') {
|
---|
| 44 | return content;
|
---|
| 45 | }
|
---|
| 46 | return content;
|
---|
| 47 | };
|
---|
| 48 |
|
---|
| 49 | /**
|
---|
| 50 | * Normalizes lines breaks.
|
---|
| 51 | * https://yaml.org/spec/1.2/spec.html#line%20break/normalization/
|
---|
| 52 | */
|
---|
| 53 | // @ts-ignore
|
---|
| 54 | const normalizeLineBreaks = val => val.replace(/\r\n/g, '\n');
|
---|
| 55 |
|
---|
| 56 | // prevent escaped line breaks from being converted to a space
|
---|
| 57 | const preventLineBreakCollapseToSpace = val => val.replace(/\\\n\s*/g, '');
|
---|
| 58 |
|
---|
| 59 | // collapse line breaks into spaces
|
---|
| 60 | const collapseLineBreakToSpace = val => {
|
---|
| 61 | /**
|
---|
| 62 | * Safari doesn't support negative lookbehind, thus we use mimicking technique:
|
---|
| 63 | *
|
---|
| 64 | * - https://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript
|
---|
| 65 | *
|
---|
| 66 | * Ideally we want to use following replace, but that's not currently possible:
|
---|
| 67 | *
|
---|
| 68 | * .replace(/[^\n]\n([^\n]+)/g, (match: string, p1: string) => ` ${p1.trimLeft()}`)
|
---|
| 69 | */
|
---|
| 70 | return val.replace(/(\n)?\n([^\n]+)/g, (match, p1, p2) => p1 ? match : ` ${p2.trimStart()}`).replace(/[\n]{2}/g, '\n');
|
---|
| 71 | };
|
---|
| 72 | const removeQuotes = curry((quoteType, val) => val.replace(new RegExp(`^${quoteType}`), '').replace(new RegExp(`${quoteType}$`), ''));
|
---|
| 73 |
|
---|
| 74 | /**
|
---|
| 75 | * Formats Flow Scalar Plain style.
|
---|
| 76 | * https://yaml.org/spec/1.2/spec.html#id2788859
|
---|
| 77 | */
|
---|
| 78 | export const formatFlowPlain = pipe(normalizeLineBreaks, trim, collapseLineBreakToSpace, split('\n'), map(trimStart), join('\n'));
|
---|
| 79 |
|
---|
| 80 | /**
|
---|
| 81 | * Formats Flow Scalar Single-Quoted style.
|
---|
| 82 | * https://yaml.org/spec/1.2/spec.html#id2788097
|
---|
| 83 | */
|
---|
| 84 |
|
---|
| 85 | export const formatFlowSingleQuoted = pipe(normalizeLineBreaks, trim, collapseLineBreakToSpace, split('\n'), map(trimStart), join('\n'), removeQuotes("'"));
|
---|
| 86 |
|
---|
| 87 | /**
|
---|
| 88 | * Formats Flow Scalar Double-Quoted style.
|
---|
| 89 | * https://yaml.org/spec/1.2/spec.html#id2787109
|
---|
| 90 | */
|
---|
| 91 | export const formatFlowDoubleQuoted = pipe(normalizeLineBreaks, trim, preventLineBreakCollapseToSpace, collapseLineBreakToSpace, unraw, split('\n'), map(trimStart), join('\n'), removeQuotes('"'));
|
---|
| 92 |
|
---|
| 93 | /**
|
---|
| 94 | * Formats Block Scalar Literal style.
|
---|
| 95 | * https://yaml.org/spec/1.2/spec.html#id2795688
|
---|
| 96 | */
|
---|
| 97 | export const formatBlockLiteral = content => {
|
---|
| 98 | const indentation = getIndentation(content);
|
---|
| 99 | const chompingIndicator = getChompingIndicator(content);
|
---|
| 100 | const normalized = normalizeLineBreaks(content);
|
---|
| 101 | const lines = tail(normalized.split('\n')); // first line only contains indicators
|
---|
| 102 | const transducer = compose(map(trimCharsStart(indentation)), map(concatRight('\n')));
|
---|
| 103 | // @ts-ignore
|
---|
| 104 | const deindented = transduce(transducer, concat, '', lines);
|
---|
| 105 | return chomp(chompingIndicator, deindented);
|
---|
| 106 | };
|
---|
| 107 |
|
---|
| 108 | /**
|
---|
| 109 | * Formats BLock Scalar Folded style.
|
---|
| 110 | * https://yaml.org/spec/1.2/spec.html#id2796251
|
---|
| 111 | */
|
---|
| 112 | export const formatBlockFolded = content => {
|
---|
| 113 | const indentation = getIndentation(content);
|
---|
| 114 | const chompingIndicator = getChompingIndicator(content);
|
---|
| 115 | const normalized = normalizeLineBreaks(content);
|
---|
| 116 | const lines = tail(normalized.split('\n')); // first line only contains indicators
|
---|
| 117 | const transducer = compose(map(trimCharsStart(indentation)), map(concatRight('\n')));
|
---|
| 118 | // @ts-ignore
|
---|
| 119 | const deindented = transduce(transducer, concat, '', lines);
|
---|
| 120 | const collapsed = collapseLineBreakToSpace(deindented);
|
---|
| 121 | return chomp(chompingIndicator, collapsed);
|
---|
| 122 | }; |
---|