Initial commit
This commit is contained in:
+50
@@ -0,0 +1,50 @@
|
||||
'use strict'
|
||||
|
||||
function isNothing (subject) {
|
||||
return (typeof subject === 'undefined') || (subject === null)
|
||||
}
|
||||
|
||||
function isObject (subject) {
|
||||
return (typeof subject === 'object') && (subject !== null)
|
||||
}
|
||||
|
||||
function toArray (sequence) {
|
||||
if (Array.isArray(sequence)) return sequence
|
||||
else if (isNothing(sequence)) return []
|
||||
|
||||
return [sequence]
|
||||
}
|
||||
|
||||
function extend (target, source) {
|
||||
if (source) {
|
||||
const sourceKeys = Object.keys(source)
|
||||
|
||||
for (let index = 0, length = sourceKeys.length; index < length; index += 1) {
|
||||
const key = sourceKeys[index]
|
||||
target[key] = source[key]
|
||||
}
|
||||
}
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
function repeat (string, count) {
|
||||
let result = ''
|
||||
|
||||
for (let cycle = 0; cycle < count; cycle += 1) {
|
||||
result += string
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function isNegativeZero (number) {
|
||||
return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number)
|
||||
}
|
||||
|
||||
module.exports.isNothing = isNothing
|
||||
module.exports.isObject = isObject
|
||||
module.exports.toArray = toArray
|
||||
module.exports.repeat = repeat
|
||||
module.exports.isNegativeZero = isNegativeZero
|
||||
module.exports.extend = extend
|
||||
+937
@@ -0,0 +1,937 @@
|
||||
'use strict'
|
||||
|
||||
const common = require('./common')
|
||||
const YAMLException = require('./exception')
|
||||
const DEFAULT_SCHEMA = require('./schema/default')
|
||||
|
||||
const _toString = Object.prototype.toString
|
||||
const _hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
|
||||
const CHAR_BOM = 0xFEFF
|
||||
const CHAR_TAB = 0x09 /* Tab */
|
||||
const CHAR_LINE_FEED = 0x0A /* LF */
|
||||
const CHAR_CARRIAGE_RETURN = 0x0D /* CR */
|
||||
const CHAR_SPACE = 0x20 /* Space */
|
||||
const CHAR_EXCLAMATION = 0x21 /* ! */
|
||||
const CHAR_DOUBLE_QUOTE = 0x22 /* " */
|
||||
const CHAR_SHARP = 0x23 /* # */
|
||||
const CHAR_PERCENT = 0x25 /* % */
|
||||
const CHAR_AMPERSAND = 0x26 /* & */
|
||||
const CHAR_SINGLE_QUOTE = 0x27 /* ' */
|
||||
const CHAR_ASTERISK = 0x2A /* * */
|
||||
const CHAR_COMMA = 0x2C /* , */
|
||||
const CHAR_MINUS = 0x2D /* - */
|
||||
const CHAR_COLON = 0x3A /* : */
|
||||
const CHAR_EQUALS = 0x3D /* = */
|
||||
const CHAR_GREATER_THAN = 0x3E /* > */
|
||||
const CHAR_QUESTION = 0x3F /* ? */
|
||||
const CHAR_COMMERCIAL_AT = 0x40 /* @ */
|
||||
const CHAR_LEFT_SQUARE_BRACKET = 0x5B /* [ */
|
||||
const CHAR_RIGHT_SQUARE_BRACKET = 0x5D /* ] */
|
||||
const CHAR_GRAVE_ACCENT = 0x60 /* ` */
|
||||
const CHAR_LEFT_CURLY_BRACKET = 0x7B /* { */
|
||||
const CHAR_VERTICAL_LINE = 0x7C /* | */
|
||||
const CHAR_RIGHT_CURLY_BRACKET = 0x7D /* } */
|
||||
|
||||
const ESCAPE_SEQUENCES = {}
|
||||
|
||||
ESCAPE_SEQUENCES[0x00] = '\\0'
|
||||
ESCAPE_SEQUENCES[0x07] = '\\a'
|
||||
ESCAPE_SEQUENCES[0x08] = '\\b'
|
||||
ESCAPE_SEQUENCES[0x09] = '\\t'
|
||||
ESCAPE_SEQUENCES[0x0A] = '\\n'
|
||||
ESCAPE_SEQUENCES[0x0B] = '\\v'
|
||||
ESCAPE_SEQUENCES[0x0C] = '\\f'
|
||||
ESCAPE_SEQUENCES[0x0D] = '\\r'
|
||||
ESCAPE_SEQUENCES[0x1B] = '\\e'
|
||||
ESCAPE_SEQUENCES[0x22] = '\\"'
|
||||
ESCAPE_SEQUENCES[0x5C] = '\\\\'
|
||||
ESCAPE_SEQUENCES[0x85] = '\\N'
|
||||
ESCAPE_SEQUENCES[0xA0] = '\\_'
|
||||
ESCAPE_SEQUENCES[0x2028] = '\\L'
|
||||
ESCAPE_SEQUENCES[0x2029] = '\\P'
|
||||
|
||||
const DEPRECATED_BOOLEANS_SYNTAX = [
|
||||
'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON',
|
||||
'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF'
|
||||
]
|
||||
|
||||
const DEPRECATED_BASE60_SYNTAX = /^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/
|
||||
|
||||
function compileStyleMap (schema, map) {
|
||||
if (map === null) return {}
|
||||
|
||||
const result = {}
|
||||
const keys = Object.keys(map)
|
||||
|
||||
for (let index = 0, length = keys.length; index < length; index += 1) {
|
||||
let tag = keys[index]
|
||||
let style = String(map[tag])
|
||||
|
||||
if (tag.slice(0, 2) === '!!') {
|
||||
tag = 'tag:yaml.org,2002:' + tag.slice(2)
|
||||
}
|
||||
const type = schema.compiledTypeMap['fallback'][tag]
|
||||
|
||||
if (type && _hasOwnProperty.call(type.styleAliases, style)) {
|
||||
style = type.styleAliases[style]
|
||||
}
|
||||
|
||||
result[tag] = style
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function encodeHex (character) {
|
||||
let handle
|
||||
let length
|
||||
|
||||
const string = character.toString(16).toUpperCase()
|
||||
|
||||
if (character <= 0xFF) {
|
||||
handle = 'x'
|
||||
length = 2
|
||||
} else if (character <= 0xFFFF) {
|
||||
handle = 'u'
|
||||
length = 4
|
||||
} else if (character <= 0xFFFFFFFF) {
|
||||
handle = 'U'
|
||||
length = 8
|
||||
} else {
|
||||
throw new YAMLException('code point within a string may not be greater than 0xFFFFFFFF')
|
||||
}
|
||||
|
||||
return '\\' + handle + common.repeat('0', length - string.length) + string
|
||||
}
|
||||
|
||||
const QUOTING_TYPE_SINGLE = 1
|
||||
const QUOTING_TYPE_DOUBLE = 2
|
||||
|
||||
function State (options) {
|
||||
this.schema = options['schema'] || DEFAULT_SCHEMA
|
||||
this.indent = Math.max(1, (options['indent'] || 2))
|
||||
this.noArrayIndent = options['noArrayIndent'] || false
|
||||
this.skipInvalid = options['skipInvalid'] || false
|
||||
this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel'])
|
||||
this.styleMap = compileStyleMap(this.schema, options['styles'] || null)
|
||||
this.sortKeys = options['sortKeys'] || false
|
||||
this.lineWidth = options['lineWidth'] || 80
|
||||
this.noRefs = options['noRefs'] || false
|
||||
this.noCompatMode = options['noCompatMode'] || false
|
||||
this.condenseFlow = options['condenseFlow'] || false
|
||||
this.quotingType = options['quotingType'] === '"' ? QUOTING_TYPE_DOUBLE : QUOTING_TYPE_SINGLE
|
||||
this.forceQuotes = options['forceQuotes'] || false
|
||||
this.replacer = typeof options['replacer'] === 'function' ? options['replacer'] : null
|
||||
|
||||
this.implicitTypes = this.schema.compiledImplicit
|
||||
this.explicitTypes = this.schema.compiledExplicit
|
||||
|
||||
this.tag = null
|
||||
this.result = ''
|
||||
|
||||
this.duplicates = []
|
||||
this.usedDuplicates = null
|
||||
}
|
||||
|
||||
// Indents every line in a string. Empty lines (\n only) are not indented.
|
||||
function indentString (string, spaces) {
|
||||
const ind = common.repeat(' ', spaces)
|
||||
let position = 0
|
||||
let result = ''
|
||||
const length = string.length
|
||||
|
||||
while (position < length) {
|
||||
let line
|
||||
const next = string.indexOf('\n', position)
|
||||
if (next === -1) {
|
||||
line = string.slice(position)
|
||||
position = length
|
||||
} else {
|
||||
line = string.slice(position, next + 1)
|
||||
position = next + 1
|
||||
}
|
||||
|
||||
if (line.length && line !== '\n') result += ind
|
||||
|
||||
result += line
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function generateNextLine (state, level) {
|
||||
return '\n' + common.repeat(' ', state.indent * level)
|
||||
}
|
||||
|
||||
function testImplicitResolving (state, str) {
|
||||
for (let index = 0, length = state.implicitTypes.length; index < length; index += 1) {
|
||||
const type = state.implicitTypes[index]
|
||||
|
||||
if (type.resolve(str)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// [33] s-white ::= s-space | s-tab
|
||||
function isWhitespace (c) {
|
||||
return c === CHAR_SPACE || c === CHAR_TAB
|
||||
}
|
||||
|
||||
// Returns true if the character can be printed without escaping.
|
||||
// From YAML 1.2: "any allowed characters known to be non-printable
|
||||
// should also be escaped. [However,] This isn’t mandatory"
|
||||
// Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029.
|
||||
function isPrintable (c) {
|
||||
return (c >= 0x00020 && c <= 0x00007E) ||
|
||||
((c >= 0x000A1 && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029) ||
|
||||
((c >= 0x0E000 && c <= 0x00FFFD) && c !== CHAR_BOM) ||
|
||||
(c >= 0x10000 && c <= 0x10FFFF)
|
||||
}
|
||||
|
||||
// [34] ns-char ::= nb-char - s-white
|
||||
// [27] nb-char ::= c-printable - b-char - c-byte-order-mark
|
||||
// [26] b-char ::= b-line-feed | b-carriage-return
|
||||
// Including s-white (for some reason, examples doesn't match specs in this aspect)
|
||||
// ns-char ::= c-printable - b-line-feed - b-carriage-return - c-byte-order-mark
|
||||
function isNsCharOrWhitespace (c) {
|
||||
return isPrintable(c) &&
|
||||
c !== CHAR_BOM &&
|
||||
// - b-char
|
||||
c !== CHAR_CARRIAGE_RETURN &&
|
||||
c !== CHAR_LINE_FEED
|
||||
}
|
||||
|
||||
// [127] ns-plain-safe(c) ::= c = flow-out ⇒ ns-plain-safe-out
|
||||
// c = flow-in ⇒ ns-plain-safe-in
|
||||
// c = block-key ⇒ ns-plain-safe-out
|
||||
// c = flow-key ⇒ ns-plain-safe-in
|
||||
// [128] ns-plain-safe-out ::= ns-char
|
||||
// [129] ns-plain-safe-in ::= ns-char - c-flow-indicator
|
||||
// [130] ns-plain-char(c) ::= ( ns-plain-safe(c) - “:” - “#” )
|
||||
// | ( /* An ns-char preceding */ “#” )
|
||||
// | ( “:” /* Followed by an ns-plain-safe(c) */ )
|
||||
function isPlainSafe (c, prev, inblock) {
|
||||
const cIsNsCharOrWhitespace = isNsCharOrWhitespace(c)
|
||||
const cIsNsChar = cIsNsCharOrWhitespace && !isWhitespace(c)
|
||||
return (
|
||||
(
|
||||
// ns-plain-safe
|
||||
inblock // c = flow-in
|
||||
? cIsNsCharOrWhitespace
|
||||
: cIsNsCharOrWhitespace &&
|
||||
// - c-flow-indicator
|
||||
c !== CHAR_COMMA &&
|
||||
c !== CHAR_LEFT_SQUARE_BRACKET &&
|
||||
c !== CHAR_RIGHT_SQUARE_BRACKET &&
|
||||
c !== CHAR_LEFT_CURLY_BRACKET &&
|
||||
c !== CHAR_RIGHT_CURLY_BRACKET
|
||||
) &&
|
||||
// ns-plain-char
|
||||
c !== CHAR_SHARP && // false on '#'
|
||||
!(prev === CHAR_COLON && !cIsNsChar)
|
||||
) || // false on ': '
|
||||
(isNsCharOrWhitespace(prev) && !isWhitespace(prev) && c === CHAR_SHARP) || // change to true on '[^ ]#'
|
||||
(prev === CHAR_COLON && cIsNsChar) // change to true on ':[^ ]'
|
||||
}
|
||||
|
||||
// Simplified test for values allowed as the first character in plain style.
|
||||
function isPlainSafeFirst (c) {
|
||||
// Uses a subset of ns-char - c-indicator
|
||||
// where ns-char = nb-char - s-white.
|
||||
// No support of ( ( “?” | “:” | “-” ) /* Followed by an ns-plain-safe(c)) */ ) part
|
||||
return isPrintable(c) &&
|
||||
c !== CHAR_BOM &&
|
||||
!isWhitespace(c) && // - s-white
|
||||
// - (c-indicator ::=
|
||||
// “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}”
|
||||
c !== CHAR_MINUS &&
|
||||
c !== CHAR_QUESTION &&
|
||||
c !== CHAR_COLON &&
|
||||
c !== CHAR_COMMA &&
|
||||
c !== CHAR_LEFT_SQUARE_BRACKET &&
|
||||
c !== CHAR_RIGHT_SQUARE_BRACKET &&
|
||||
c !== CHAR_LEFT_CURLY_BRACKET &&
|
||||
c !== CHAR_RIGHT_CURLY_BRACKET &&
|
||||
// | “#” | “&” | “*” | “!” | “|” | “=” | “>” | “'” | “"”
|
||||
c !== CHAR_SHARP &&
|
||||
c !== CHAR_AMPERSAND &&
|
||||
c !== CHAR_ASTERISK &&
|
||||
c !== CHAR_EXCLAMATION &&
|
||||
c !== CHAR_VERTICAL_LINE &&
|
||||
c !== CHAR_EQUALS &&
|
||||
c !== CHAR_GREATER_THAN &&
|
||||
c !== CHAR_SINGLE_QUOTE &&
|
||||
c !== CHAR_DOUBLE_QUOTE &&
|
||||
// | “%” | “@” | “`”)
|
||||
c !== CHAR_PERCENT &&
|
||||
c !== CHAR_COMMERCIAL_AT &&
|
||||
c !== CHAR_GRAVE_ACCENT
|
||||
}
|
||||
|
||||
// Simplified test for values allowed as the last character in plain style.
|
||||
function isPlainSafeLast (c) {
|
||||
// just not whitespace or colon, it will be checked to be plain character later
|
||||
return !isWhitespace(c) && c !== CHAR_COLON
|
||||
}
|
||||
|
||||
// Same as 'string'.codePointAt(pos), but works in older browsers.
|
||||
function codePointAt (string, pos) {
|
||||
const first = string.charCodeAt(pos)
|
||||
let second
|
||||
|
||||
if (first >= 0xD800 && first <= 0xDBFF && pos + 1 < string.length) {
|
||||
second = string.charCodeAt(pos + 1)
|
||||
if (second >= 0xDC00 && second <= 0xDFFF) {
|
||||
// https://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
|
||||
return (first - 0xD800) * 0x400 + second - 0xDC00 + 0x10000
|
||||
}
|
||||
}
|
||||
return first
|
||||
}
|
||||
|
||||
// Determines whether block indentation indicator is required.
|
||||
function needIndentIndicator (string) {
|
||||
const leadingSpaceRe = /^\n* /
|
||||
return leadingSpaceRe.test(string)
|
||||
}
|
||||
|
||||
const STYLE_PLAIN = 1
|
||||
const STYLE_SINGLE = 2
|
||||
const STYLE_LITERAL = 3
|
||||
const STYLE_FOLDED = 4
|
||||
const STYLE_DOUBLE = 5
|
||||
|
||||
// Determines which scalar styles are possible and returns the preferred style.
|
||||
// lineWidth = -1 => no limit.
|
||||
// Pre-conditions: str.length > 0.
|
||||
// Post-conditions:
|
||||
// STYLE_PLAIN or STYLE_SINGLE => no \n are in the string.
|
||||
// STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1).
|
||||
// STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1).
|
||||
function chooseScalarStyle (string, singleLineOnly, indentPerLevel, lineWidth,
|
||||
testAmbiguousType, quotingType, forceQuotes, inblock) {
|
||||
let i
|
||||
let char = 0
|
||||
let prevChar = null
|
||||
let hasLineBreak = false
|
||||
let hasFoldableLine = false // only checked if shouldTrackWidth
|
||||
const shouldTrackWidth = lineWidth !== -1
|
||||
let previousLineBreak = -1 // count the first line correctly
|
||||
let plain = isPlainSafeFirst(codePointAt(string, 0)) &&
|
||||
isPlainSafeLast(codePointAt(string, string.length - 1))
|
||||
|
||||
if (singleLineOnly || forceQuotes) {
|
||||
// Case: no block styles.
|
||||
// Check for disallowed characters to rule out plain and single.
|
||||
for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
|
||||
char = codePointAt(string, i)
|
||||
if (!isPrintable(char)) {
|
||||
return STYLE_DOUBLE
|
||||
}
|
||||
plain = plain && isPlainSafe(char, prevChar, inblock)
|
||||
prevChar = char
|
||||
}
|
||||
} else {
|
||||
// Case: block styles permitted.
|
||||
for (i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
|
||||
char = codePointAt(string, i)
|
||||
if (char === CHAR_LINE_FEED) {
|
||||
hasLineBreak = true
|
||||
// Check if any line can be folded.
|
||||
if (shouldTrackWidth) {
|
||||
hasFoldableLine = hasFoldableLine ||
|
||||
// Foldable line = too long, and not more-indented.
|
||||
(i - previousLineBreak - 1 > lineWidth &&
|
||||
string[previousLineBreak + 1] !== ' ')
|
||||
previousLineBreak = i
|
||||
}
|
||||
} else if (!isPrintable(char)) {
|
||||
return STYLE_DOUBLE
|
||||
}
|
||||
plain = plain && isPlainSafe(char, prevChar, inblock)
|
||||
prevChar = char
|
||||
}
|
||||
// in case the end is missing a \n
|
||||
hasFoldableLine = hasFoldableLine || (shouldTrackWidth &&
|
||||
(i - previousLineBreak - 1 > lineWidth &&
|
||||
string[previousLineBreak + 1] !== ' '))
|
||||
}
|
||||
// Although every style can represent \n without escaping, prefer block styles
|
||||
// for multiline, since they're more readable and they don't add empty lines.
|
||||
// Also prefer folding a super-long line.
|
||||
if (!hasLineBreak && !hasFoldableLine) {
|
||||
// Strings interpretable as another type have to be quoted;
|
||||
// e.g. the string 'true' vs. the boolean true.
|
||||
if (plain && !forceQuotes && !testAmbiguousType(string)) {
|
||||
return STYLE_PLAIN
|
||||
}
|
||||
return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE
|
||||
}
|
||||
// Edge case: block indentation indicator can only have one digit.
|
||||
if (indentPerLevel > 9 && needIndentIndicator(string)) {
|
||||
return STYLE_DOUBLE
|
||||
}
|
||||
// At this point we know block styles are valid.
|
||||
// Prefer literal style unless we want to fold.
|
||||
if (!forceQuotes) {
|
||||
return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL
|
||||
}
|
||||
return quotingType === QUOTING_TYPE_DOUBLE ? STYLE_DOUBLE : STYLE_SINGLE
|
||||
}
|
||||
|
||||
// Note: line breaking/folding is implemented for only the folded style.
|
||||
// NB. We drop the last trailing newline (if any) of a returned block scalar
|
||||
// since the dumper adds its own newline. This always works:
|
||||
// • No ending newline => unaffected; already using strip "-" chomping.
|
||||
// • Ending newline => removed then restored.
|
||||
// Importantly, this keeps the "+" chomp indicator from gaining an extra line.
|
||||
function writeScalar (state, string, level, iskey, inblock) {
|
||||
state.dump = (function () {
|
||||
if (string.length === 0) {
|
||||
return state.quotingType === QUOTING_TYPE_DOUBLE ? '""' : "''"
|
||||
}
|
||||
if (!state.noCompatMode) {
|
||||
if (DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1 || DEPRECATED_BASE60_SYNTAX.test(string)) {
|
||||
return state.quotingType === QUOTING_TYPE_DOUBLE ? ('"' + string + '"') : ("'" + string + "'")
|
||||
}
|
||||
}
|
||||
|
||||
const indent = state.indent * Math.max(1, level) // no 0-indent scalars
|
||||
// As indentation gets deeper, let the width decrease monotonically
|
||||
// to the lower bound min(state.lineWidth, 40).
|
||||
// Note that this implies
|
||||
// state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound.
|
||||
// state.lineWidth > 40 + state.indent: width decreases until the lower bound.
|
||||
// This behaves better than a constant minimum width which disallows narrower options,
|
||||
// or an indent threshold which causes the width to suddenly increase.
|
||||
const lineWidth = (state.lineWidth === -1)
|
||||
? -1
|
||||
: Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent)
|
||||
|
||||
// Without knowing if keys are implicit/explicit, assume implicit for safety.
|
||||
const singleLineOnly = iskey ||
|
||||
// No block styles in flow mode.
|
||||
(state.flowLevel > -1 && level >= state.flowLevel)
|
||||
function testAmbiguity (string) {
|
||||
return testImplicitResolving(state, string)
|
||||
}
|
||||
|
||||
switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth,
|
||||
testAmbiguity, state.quotingType, state.forceQuotes && !iskey, inblock)) {
|
||||
case STYLE_PLAIN:
|
||||
return string
|
||||
case STYLE_SINGLE:
|
||||
return "'" + string.replace(/'/g, "''") + "'"
|
||||
case STYLE_LITERAL:
|
||||
return '|' + blockHeader(string, state.indent) +
|
||||
dropEndingNewline(indentString(string, indent))
|
||||
case STYLE_FOLDED:
|
||||
return '>' + blockHeader(string, state.indent) +
|
||||
dropEndingNewline(indentString(foldString(string, lineWidth), indent))
|
||||
case STYLE_DOUBLE:
|
||||
return '"' + escapeString(string, lineWidth) + '"'
|
||||
default:
|
||||
throw new YAMLException('impossible error: invalid scalar style')
|
||||
}
|
||||
}())
|
||||
}
|
||||
|
||||
// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.
|
||||
function blockHeader (string, indentPerLevel) {
|
||||
const indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ''
|
||||
|
||||
// note the special case: the string '\n' counts as a "trailing" empty line.
|
||||
const clip = string[string.length - 1] === '\n'
|
||||
const keep = clip && (string[string.length - 2] === '\n' || string === '\n')
|
||||
const chomp = keep ? '+' : (clip ? '' : '-')
|
||||
|
||||
return indentIndicator + chomp + '\n'
|
||||
}
|
||||
|
||||
// (See the note for writeScalar.)
|
||||
function dropEndingNewline (string) {
|
||||
return string[string.length - 1] === '\n' ? string.slice(0, -1) : string
|
||||
}
|
||||
|
||||
// Note: a long line without a suitable break point will exceed the width limit.
|
||||
// Pre-conditions: every char in str isPrintable, str.length > 0, width > 0.
|
||||
function foldString (string, width) {
|
||||
// In folded style, $k$ consecutive newlines output as $k+1$ newlines—
|
||||
// unless they're before or after a more-indented line, or at the very
|
||||
// beginning or end, in which case $k$ maps to $k$.
|
||||
// Therefore, parse each chunk as newline(s) followed by a content line.
|
||||
const lineRe = /(\n+)([^\n]*)/g
|
||||
|
||||
// first line (possibly an empty line)
|
||||
let result = (function () {
|
||||
let nextLF = string.indexOf('\n')
|
||||
nextLF = nextLF !== -1 ? nextLF : string.length
|
||||
lineRe.lastIndex = nextLF
|
||||
return foldLine(string.slice(0, nextLF), width)
|
||||
}())
|
||||
// If we haven't reached the first content line yet, don't add an extra \n.
|
||||
let prevMoreIndented = string[0] === '\n' || string[0] === ' '
|
||||
let moreIndented
|
||||
|
||||
// rest of the lines
|
||||
let match
|
||||
while ((match = lineRe.exec(string))) {
|
||||
const prefix = match[1]
|
||||
const line = match[2]
|
||||
|
||||
moreIndented = (line[0] === ' ')
|
||||
result += prefix +
|
||||
((!prevMoreIndented && !moreIndented && line !== '') ? '\n' : '') +
|
||||
foldLine(line, width)
|
||||
prevMoreIndented = moreIndented
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Greedy line breaking.
|
||||
// Picks the longest line under the limit each time,
|
||||
// otherwise settles for the shortest line over the limit.
|
||||
// NB. More-indented lines *cannot* be folded, as that would add an extra \n.
|
||||
function foldLine (line, width) {
|
||||
if (line === '' || line[0] === ' ') return line
|
||||
|
||||
// Since a more-indented line adds a \n, breaks can't be followed by a space.
|
||||
const breakRe = / [^ ]/g // note: the match index will always be <= length-2.
|
||||
let match
|
||||
// start is an inclusive index. end, curr, and next are exclusive.
|
||||
let start = 0
|
||||
let end
|
||||
let curr = 0
|
||||
let next = 0
|
||||
let result = ''
|
||||
|
||||
// Invariants: 0 <= start <= length-1.
|
||||
// 0 <= curr <= next <= max(0, length-2). curr - start <= width.
|
||||
// Inside the loop:
|
||||
// A match implies length >= 2, so curr and next are <= length-2.
|
||||
while ((match = breakRe.exec(line))) {
|
||||
next = match.index
|
||||
// maintain invariant: curr - start <= width
|
||||
if (next - start > width) {
|
||||
end = (curr > start) ? curr : next // derive end <= length-2
|
||||
result += '\n' + line.slice(start, end)
|
||||
// skip the space that was output as \n
|
||||
start = end + 1 // derive start <= length-1
|
||||
}
|
||||
curr = next
|
||||
}
|
||||
|
||||
// By the invariants, start <= length-1, so there is something left over.
|
||||
// It is either the whole string or a part starting from non-whitespace.
|
||||
result += '\n'
|
||||
// Insert a break if the remainder is too long and there is a break available.
|
||||
if (line.length - start > width && curr > start) {
|
||||
result += line.slice(start, curr) + '\n' + line.slice(curr + 1)
|
||||
} else {
|
||||
result += line.slice(start)
|
||||
}
|
||||
|
||||
return result.slice(1) // drop extra \n joiner
|
||||
}
|
||||
|
||||
// Escapes a double-quoted string.
|
||||
function escapeString (string) {
|
||||
let result = ''
|
||||
let char = 0
|
||||
|
||||
for (let i = 0; i < string.length; char >= 0x10000 ? i += 2 : i++) {
|
||||
char = codePointAt(string, i)
|
||||
const escapeSeq = ESCAPE_SEQUENCES[char]
|
||||
|
||||
if (!escapeSeq && isPrintable(char)) {
|
||||
result += string[i]
|
||||
if (char >= 0x10000) result += string[i + 1]
|
||||
} else {
|
||||
result += escapeSeq || encodeHex(char)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function writeFlowSequence (state, level, object) {
|
||||
let _result = ''
|
||||
const _tag = state.tag
|
||||
|
||||
for (let index = 0, length = object.length; index < length; index += 1) {
|
||||
let value = object[index]
|
||||
|
||||
if (state.replacer) {
|
||||
value = state.replacer.call(object, String(index), value)
|
||||
}
|
||||
|
||||
// Write only valid elements, put null instead of invalid elements.
|
||||
if (writeNode(state, level, value, false, false) ||
|
||||
(typeof value === 'undefined' &&
|
||||
writeNode(state, level, null, false, false))) {
|
||||
if (_result !== '') _result += ',' + (!state.condenseFlow ? ' ' : '')
|
||||
_result += state.dump
|
||||
}
|
||||
}
|
||||
|
||||
state.tag = _tag
|
||||
state.dump = '[' + _result + ']'
|
||||
}
|
||||
|
||||
function writeBlockSequence (state, level, object, compact) {
|
||||
let _result = ''
|
||||
const _tag = state.tag
|
||||
|
||||
for (let index = 0, length = object.length; index < length; index += 1) {
|
||||
let value = object[index]
|
||||
|
||||
if (state.replacer) {
|
||||
value = state.replacer.call(object, String(index), value)
|
||||
}
|
||||
|
||||
// Write only valid elements, put null instead of invalid elements.
|
||||
if (writeNode(state, level + 1, value, true, true, false, true) ||
|
||||
(typeof value === 'undefined' &&
|
||||
writeNode(state, level + 1, null, true, true, false, true))) {
|
||||
if (!compact || _result !== '') {
|
||||
_result += generateNextLine(state, level)
|
||||
}
|
||||
|
||||
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
|
||||
_result += '-'
|
||||
} else {
|
||||
_result += '- '
|
||||
}
|
||||
|
||||
_result += state.dump
|
||||
}
|
||||
}
|
||||
|
||||
state.tag = _tag
|
||||
state.dump = _result || '[]' // Empty sequence if no valid values.
|
||||
}
|
||||
|
||||
function writeFlowMapping (state, level, object) {
|
||||
let _result = ''
|
||||
const _tag = state.tag
|
||||
const objectKeyList = Object.keys(object)
|
||||
|
||||
for (let index = 0, length = objectKeyList.length; index < length; index += 1) {
|
||||
let pairBuffer = ''
|
||||
if (_result !== '') pairBuffer += ', '
|
||||
|
||||
if (state.condenseFlow) pairBuffer += '"'
|
||||
|
||||
const objectKey = objectKeyList[index]
|
||||
let objectValue = object[objectKey]
|
||||
|
||||
if (state.replacer) {
|
||||
objectValue = state.replacer.call(object, objectKey, objectValue)
|
||||
}
|
||||
|
||||
if (!writeNode(state, level, objectKey, false, false)) {
|
||||
continue // Skip this pair because of invalid key;
|
||||
}
|
||||
|
||||
if (state.dump.length > 1024) pairBuffer += '? '
|
||||
|
||||
pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' ')
|
||||
|
||||
if (!writeNode(state, level, objectValue, false, false)) {
|
||||
continue // Skip this pair because of invalid value.
|
||||
}
|
||||
|
||||
pairBuffer += state.dump
|
||||
|
||||
// Both key and value are valid.
|
||||
_result += pairBuffer
|
||||
}
|
||||
|
||||
state.tag = _tag
|
||||
state.dump = '{' + _result + '}'
|
||||
}
|
||||
|
||||
function writeBlockMapping (state, level, object, compact) {
|
||||
let _result = ''
|
||||
const _tag = state.tag
|
||||
const objectKeyList = Object.keys(object)
|
||||
|
||||
// Allow sorting keys so that the output file is deterministic
|
||||
if (state.sortKeys === true) {
|
||||
// Default sorting
|
||||
objectKeyList.sort()
|
||||
} else if (typeof state.sortKeys === 'function') {
|
||||
// Custom sort function
|
||||
objectKeyList.sort(state.sortKeys)
|
||||
} else if (state.sortKeys) {
|
||||
// Something is wrong
|
||||
throw new YAMLException('sortKeys must be a boolean or a function')
|
||||
}
|
||||
|
||||
for (let index = 0, length = objectKeyList.length; index < length; index += 1) {
|
||||
let pairBuffer = ''
|
||||
|
||||
if (!compact || _result !== '') {
|
||||
pairBuffer += generateNextLine(state, level)
|
||||
}
|
||||
|
||||
const objectKey = objectKeyList[index]
|
||||
let objectValue = object[objectKey]
|
||||
|
||||
if (state.replacer) {
|
||||
objectValue = state.replacer.call(object, objectKey, objectValue)
|
||||
}
|
||||
|
||||
if (!writeNode(state, level + 1, objectKey, true, true, true)) {
|
||||
continue // Skip this pair because of invalid key.
|
||||
}
|
||||
|
||||
const explicitPair = (state.tag !== null && state.tag !== '?') ||
|
||||
(state.dump && state.dump.length > 1024)
|
||||
|
||||
if (explicitPair) {
|
||||
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
|
||||
pairBuffer += '?'
|
||||
} else {
|
||||
pairBuffer += '? '
|
||||
}
|
||||
}
|
||||
|
||||
pairBuffer += state.dump
|
||||
|
||||
if (explicitPair) {
|
||||
pairBuffer += generateNextLine(state, level)
|
||||
}
|
||||
|
||||
if (!writeNode(state, level + 1, objectValue, true, explicitPair)) {
|
||||
continue // Skip this pair because of invalid value.
|
||||
}
|
||||
|
||||
if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) {
|
||||
pairBuffer += ':'
|
||||
} else {
|
||||
pairBuffer += ': '
|
||||
}
|
||||
|
||||
pairBuffer += state.dump
|
||||
|
||||
// Both key and value are valid.
|
||||
_result += pairBuffer
|
||||
}
|
||||
|
||||
state.tag = _tag
|
||||
state.dump = _result || '{}' // Empty mapping if no valid pairs.
|
||||
}
|
||||
|
||||
function detectType (state, object, explicit) {
|
||||
const typeList = explicit ? state.explicitTypes : state.implicitTypes
|
||||
|
||||
for (let index = 0, length = typeList.length; index < length; index += 1) {
|
||||
const type = typeList[index]
|
||||
|
||||
if ((type.instanceOf || type.predicate) &&
|
||||
(!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) &&
|
||||
(!type.predicate || type.predicate(object))) {
|
||||
if (explicit) {
|
||||
if (type.multi && type.representName) {
|
||||
state.tag = type.representName(object)
|
||||
} else {
|
||||
state.tag = type.tag
|
||||
}
|
||||
} else {
|
||||
state.tag = '?'
|
||||
}
|
||||
|
||||
if (type.represent) {
|
||||
const style = state.styleMap[type.tag] || type.defaultStyle
|
||||
|
||||
let _result
|
||||
if (_toString.call(type.represent) === '[object Function]') {
|
||||
_result = type.represent(object, style)
|
||||
} else if (_hasOwnProperty.call(type.represent, style)) {
|
||||
_result = type.represent[style](object, style)
|
||||
} else {
|
||||
throw new YAMLException('!<' + type.tag + '> tag resolver accepts not "' + style + '" style')
|
||||
}
|
||||
|
||||
state.dump = _result
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Serializes `object` and writes it to global `result`.
|
||||
// Returns true on success, or false on invalid object.
|
||||
//
|
||||
function writeNode (state, level, object, block, compact, iskey, isblockseq) {
|
||||
state.tag = null
|
||||
state.dump = object
|
||||
|
||||
if (!detectType(state, object, false)) {
|
||||
detectType(state, object, true)
|
||||
}
|
||||
|
||||
const type = _toString.call(state.dump)
|
||||
const inblock = block
|
||||
|
||||
if (block) {
|
||||
block = (state.flowLevel < 0 || state.flowLevel > level)
|
||||
}
|
||||
|
||||
const objectOrArray = type === '[object Object]' || type === '[object Array]'
|
||||
let duplicateIndex
|
||||
let duplicate
|
||||
|
||||
if (objectOrArray) {
|
||||
duplicateIndex = state.duplicates.indexOf(object)
|
||||
duplicate = duplicateIndex !== -1
|
||||
}
|
||||
|
||||
if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) {
|
||||
compact = false
|
||||
}
|
||||
|
||||
if (duplicate && state.usedDuplicates[duplicateIndex]) {
|
||||
state.dump = '*ref_' + duplicateIndex
|
||||
} else {
|
||||
if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) {
|
||||
state.usedDuplicates[duplicateIndex] = true
|
||||
}
|
||||
if (type === '[object Object]') {
|
||||
if (block && (Object.keys(state.dump).length !== 0)) {
|
||||
writeBlockMapping(state, level, state.dump, compact)
|
||||
if (duplicate) {
|
||||
state.dump = '&ref_' + duplicateIndex + state.dump
|
||||
}
|
||||
} else {
|
||||
writeFlowMapping(state, level, state.dump)
|
||||
if (duplicate) {
|
||||
state.dump = '&ref_' + duplicateIndex + ' ' + state.dump
|
||||
}
|
||||
}
|
||||
} else if (type === '[object Array]') {
|
||||
if (block && (state.dump.length !== 0)) {
|
||||
if (state.noArrayIndent && !isblockseq && level > 0) {
|
||||
writeBlockSequence(state, level - 1, state.dump, compact)
|
||||
} else {
|
||||
writeBlockSequence(state, level, state.dump, compact)
|
||||
}
|
||||
if (duplicate) {
|
||||
state.dump = '&ref_' + duplicateIndex + state.dump
|
||||
}
|
||||
} else {
|
||||
writeFlowSequence(state, level, state.dump)
|
||||
if (duplicate) {
|
||||
state.dump = '&ref_' + duplicateIndex + ' ' + state.dump
|
||||
}
|
||||
}
|
||||
} else if (type === '[object String]') {
|
||||
if (state.tag !== '?') {
|
||||
writeScalar(state, state.dump, level, iskey, inblock)
|
||||
}
|
||||
} else if (type === '[object Undefined]') {
|
||||
return false
|
||||
} else {
|
||||
if (state.skipInvalid) return false
|
||||
throw new YAMLException('unacceptable kind of an object to dump ' + type)
|
||||
}
|
||||
|
||||
if (state.tag !== null && state.tag !== '?') {
|
||||
// Need to encode all characters except those allowed by the spec:
|
||||
//
|
||||
// [35] ns-dec-digit ::= [#x30-#x39] /* 0-9 */
|
||||
// [36] ns-hex-digit ::= ns-dec-digit
|
||||
// | [#x41-#x46] /* A-F */ | [#x61-#x66] /* a-f */
|
||||
// [37] ns-ascii-letter ::= [#x41-#x5A] /* A-Z */ | [#x61-#x7A] /* a-z */
|
||||
// [38] ns-word-char ::= ns-dec-digit | ns-ascii-letter | “-”
|
||||
// [39] ns-uri-char ::= “%” ns-hex-digit ns-hex-digit | ns-word-char | “#”
|
||||
// | “;” | “/” | “?” | “:” | “@” | “&” | “=” | “+” | “$” | “,”
|
||||
// | “_” | “.” | “!” | “~” | “*” | “'” | “(” | “)” | “[” | “]”
|
||||
//
|
||||
// Also need to encode '!' because it has special meaning (end of tag prefix).
|
||||
//
|
||||
let tagStr = encodeURI(
|
||||
state.tag[0] === '!' ? state.tag.slice(1) : state.tag
|
||||
).replace(/!/g, '%21')
|
||||
|
||||
if (state.tag[0] === '!') {
|
||||
tagStr = '!' + tagStr
|
||||
} else if (tagStr.slice(0, 18) === 'tag:yaml.org,2002:') {
|
||||
tagStr = '!!' + tagStr.slice(18)
|
||||
} else {
|
||||
tagStr = '!<' + tagStr + '>'
|
||||
}
|
||||
|
||||
state.dump = tagStr + ' ' + state.dump
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function getDuplicateReferences (object, state) {
|
||||
const objects = []
|
||||
const duplicatesIndexes = []
|
||||
|
||||
inspectNode(object, objects, duplicatesIndexes)
|
||||
|
||||
const length = duplicatesIndexes.length
|
||||
for (let index = 0; index < length; index += 1) {
|
||||
state.duplicates.push(objects[duplicatesIndexes[index]])
|
||||
}
|
||||
state.usedDuplicates = new Array(length)
|
||||
}
|
||||
|
||||
function inspectNode (object, objects, duplicatesIndexes) {
|
||||
if (object !== null && typeof object === 'object') {
|
||||
const index = objects.indexOf(object)
|
||||
if (index !== -1) {
|
||||
if (duplicatesIndexes.indexOf(index) === -1) {
|
||||
duplicatesIndexes.push(index)
|
||||
}
|
||||
} else {
|
||||
objects.push(object)
|
||||
|
||||
if (Array.isArray(object)) {
|
||||
for (let i = 0, length = object.length; i < length; i += 1) {
|
||||
inspectNode(object[i], objects, duplicatesIndexes)
|
||||
}
|
||||
} else {
|
||||
const objectKeyList = Object.keys(object)
|
||||
|
||||
for (let i = 0, length = objectKeyList.length; i < length; i += 1) {
|
||||
inspectNode(object[objectKeyList[i]], objects, duplicatesIndexes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dump (input, options) {
|
||||
options = options || {}
|
||||
|
||||
const state = new State(options)
|
||||
|
||||
if (!state.noRefs) getDuplicateReferences(input, state)
|
||||
|
||||
let value = input
|
||||
|
||||
if (state.replacer) {
|
||||
value = state.replacer.call({ '': value }, '', value)
|
||||
}
|
||||
|
||||
if (writeNode(state, 0, value, true, true)) return state.dump + '\n'
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
module.exports.dump = dump
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// YAML error class. http://stackoverflow.com/questions/8458984
|
||||
//
|
||||
'use strict'
|
||||
|
||||
function formatError (exception, compact) {
|
||||
let where = ''
|
||||
const message = exception.reason || '(unknown reason)'
|
||||
|
||||
if (!exception.mark) return message
|
||||
|
||||
if (exception.mark.name) {
|
||||
where += 'in "' + exception.mark.name + '" '
|
||||
}
|
||||
|
||||
where += '(' + (exception.mark.line + 1) + ':' + (exception.mark.column + 1) + ')'
|
||||
|
||||
if (!compact && exception.mark.snippet) {
|
||||
where += '\n\n' + exception.mark.snippet
|
||||
}
|
||||
|
||||
return message + ' ' + where
|
||||
}
|
||||
|
||||
function YAMLException (reason, mark) {
|
||||
// Super constructor
|
||||
Error.call(this)
|
||||
|
||||
this.name = 'YAMLException'
|
||||
this.reason = reason
|
||||
this.mark = mark
|
||||
this.message = formatError(this, false)
|
||||
|
||||
// Include stack trace in error object
|
||||
if (Error.captureStackTrace) {
|
||||
// Chrome and NodeJS
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
} else {
|
||||
// FF, IE 10+ and Safari 6+. Fallback for others
|
||||
this.stack = (new Error()).stack || ''
|
||||
}
|
||||
}
|
||||
|
||||
// Inherit from Error
|
||||
YAMLException.prototype = Object.create(Error.prototype)
|
||||
YAMLException.prototype.constructor = YAMLException
|
||||
|
||||
YAMLException.prototype.toString = function toString (compact) {
|
||||
return this.name + ': ' + formatError(this, compact)
|
||||
}
|
||||
|
||||
module.exports = YAMLException
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import yaml from '../index.js'
|
||||
|
||||
const {
|
||||
Type,
|
||||
Schema,
|
||||
FAILSAFE_SCHEMA,
|
||||
JSON_SCHEMA,
|
||||
CORE_SCHEMA,
|
||||
DEFAULT_SCHEMA,
|
||||
load,
|
||||
loadAll,
|
||||
dump,
|
||||
YAMLException,
|
||||
types,
|
||||
safeLoad,
|
||||
safeLoadAll,
|
||||
safeDump
|
||||
} = yaml
|
||||
|
||||
export {
|
||||
Type,
|
||||
Schema,
|
||||
FAILSAFE_SCHEMA,
|
||||
JSON_SCHEMA,
|
||||
CORE_SCHEMA,
|
||||
DEFAULT_SCHEMA,
|
||||
load,
|
||||
loadAll,
|
||||
dump,
|
||||
YAMLException,
|
||||
types,
|
||||
safeLoad,
|
||||
safeLoadAll,
|
||||
safeDump
|
||||
}
|
||||
|
||||
export default yaml
|
||||
+1794
File diff suppressed because it is too large
Load Diff
+109
@@ -0,0 +1,109 @@
|
||||
'use strict'
|
||||
|
||||
const YAMLException = require('./exception')
|
||||
const Type = require('./type')
|
||||
|
||||
function compileList (schema, name) {
|
||||
const result = []
|
||||
|
||||
schema[name].forEach(function (currentType) {
|
||||
let newIndex = result.length
|
||||
|
||||
result.forEach(function (previousType, previousIndex) {
|
||||
if (previousType.tag === currentType.tag &&
|
||||
previousType.kind === currentType.kind &&
|
||||
previousType.multi === currentType.multi) {
|
||||
newIndex = previousIndex
|
||||
}
|
||||
})
|
||||
|
||||
result[newIndex] = currentType
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function compileMap (/* lists... */) {
|
||||
const result = {
|
||||
scalar: {},
|
||||
sequence: {},
|
||||
mapping: {},
|
||||
fallback: {},
|
||||
multi: {
|
||||
scalar: [],
|
||||
sequence: [],
|
||||
mapping: [],
|
||||
fallback: []
|
||||
}
|
||||
}
|
||||
function collectType (type) {
|
||||
if (type.multi) {
|
||||
result.multi[type.kind].push(type)
|
||||
result.multi['fallback'].push(type)
|
||||
} else {
|
||||
result[type.kind][type.tag] = result['fallback'][type.tag] = type
|
||||
}
|
||||
}
|
||||
|
||||
for (let index = 0, length = arguments.length; index < length; index += 1) {
|
||||
arguments[index].forEach(collectType)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function Schema (definition) {
|
||||
return this.extend(definition)
|
||||
}
|
||||
|
||||
Schema.prototype.extend = function extend (definition) {
|
||||
let implicit = []
|
||||
let explicit = []
|
||||
|
||||
if (definition instanceof Type) {
|
||||
// Schema.extend(type)
|
||||
explicit.push(definition)
|
||||
} else if (Array.isArray(definition)) {
|
||||
// Schema.extend([ type1, type2, ... ])
|
||||
explicit = explicit.concat(definition)
|
||||
} else if (definition && (Array.isArray(definition.implicit) || Array.isArray(definition.explicit))) {
|
||||
// Schema.extend({ explicit: [ type1, type2, ... ], implicit: [ type1, type2, ... ] })
|
||||
if (definition.implicit) implicit = implicit.concat(definition.implicit)
|
||||
if (definition.explicit) explicit = explicit.concat(definition.explicit)
|
||||
} else {
|
||||
throw new YAMLException('Schema.extend argument should be a Type, [ Type ], ' +
|
||||
'or a schema definition ({ implicit: [...], explicit: [...] })')
|
||||
}
|
||||
|
||||
implicit.forEach(function (type) {
|
||||
if (!(type instanceof Type)) {
|
||||
throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.')
|
||||
}
|
||||
|
||||
if (type.loadKind && type.loadKind !== 'scalar') {
|
||||
throw new YAMLException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.')
|
||||
}
|
||||
|
||||
if (type.multi) {
|
||||
throw new YAMLException('There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.')
|
||||
}
|
||||
})
|
||||
|
||||
explicit.forEach(function (type) {
|
||||
if (!(type instanceof Type)) {
|
||||
throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.')
|
||||
}
|
||||
})
|
||||
|
||||
const result = Object.create(Schema.prototype)
|
||||
|
||||
result.implicit = (this.implicit || []).concat(implicit)
|
||||
result.explicit = (this.explicit || []).concat(explicit)
|
||||
|
||||
result.compiledImplicit = compileList(result, 'implicit')
|
||||
result.compiledExplicit = compileList(result, 'explicit')
|
||||
result.compiledTypeMap = compileMap(result.compiledImplicit, result.compiledExplicit)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = Schema
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// Standard YAML's Core schema.
|
||||
// http://www.yaml.org/spec/1.2/spec.html#id2804923
|
||||
//
|
||||
// NOTE: JS-YAML does not support schema-specific tag resolution restrictions.
|
||||
// So, Core schema has no distinctions from JSON schema is JS-YAML.
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = require('./json')
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// JS-YAML's default schema for `safeLoad` function.
|
||||
// It is not described in the YAML specification.
|
||||
//
|
||||
// This schema is based on standard YAML's Core schema and includes most of
|
||||
// extra types described at YAML tag repository. (http://yaml.org/type/)
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = require('./core').extend({
|
||||
implicit: [
|
||||
require('../type/timestamp'),
|
||||
require('../type/merge')
|
||||
],
|
||||
explicit: [
|
||||
require('../type/binary'),
|
||||
require('../type/omap'),
|
||||
require('../type/pairs'),
|
||||
require('../type/set')
|
||||
]
|
||||
})
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Standard YAML's Failsafe schema.
|
||||
// http://www.yaml.org/spec/1.2/spec.html#id2802346
|
||||
|
||||
'use strict'
|
||||
|
||||
const Schema = require('../schema')
|
||||
|
||||
module.exports = new Schema({
|
||||
explicit: [
|
||||
require('../type/str'),
|
||||
require('../type/seq'),
|
||||
require('../type/map')
|
||||
]
|
||||
})
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Standard YAML's JSON schema.
|
||||
// http://www.yaml.org/spec/1.2/spec.html#id2803231
|
||||
//
|
||||
// NOTE: JS-YAML does not support schema-specific tag resolution restrictions.
|
||||
// So, this schema is not such strict as defined in the YAML specification.
|
||||
// It allows numbers in binary notaion, use `Null` and `NULL` as `null`, etc.
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = require('./failsafe').extend({
|
||||
implicit: [
|
||||
require('../type/null'),
|
||||
require('../type/bool'),
|
||||
require('../type/int'),
|
||||
require('../type/float')
|
||||
]
|
||||
})
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
'use strict'
|
||||
|
||||
const common = require('./common')
|
||||
|
||||
// get snippet for a single line, respecting maxLength
|
||||
function getLine (buffer, lineStart, lineEnd, position, maxLineLength) {
|
||||
let head = ''
|
||||
let tail = ''
|
||||
const maxHalfLength = Math.floor(maxLineLength / 2) - 1
|
||||
|
||||
if (position - lineStart > maxHalfLength) {
|
||||
head = ' ... '
|
||||
lineStart = position - maxHalfLength + head.length
|
||||
}
|
||||
|
||||
if (lineEnd - position > maxHalfLength) {
|
||||
tail = ' ...'
|
||||
lineEnd = position + maxHalfLength - tail.length
|
||||
}
|
||||
|
||||
return {
|
||||
str: head + buffer.slice(lineStart, lineEnd).replace(/\t/g, '→') + tail,
|
||||
pos: position - lineStart + head.length // relative position
|
||||
}
|
||||
}
|
||||
|
||||
function padStart (string, max) {
|
||||
return common.repeat(' ', max - string.length) + string
|
||||
}
|
||||
|
||||
function makeSnippet (mark, options) {
|
||||
options = Object.create(options || null)
|
||||
|
||||
if (!mark.buffer) return null
|
||||
|
||||
if (!options.maxLength) options.maxLength = 79
|
||||
if (typeof options.indent !== 'number') options.indent = 1
|
||||
if (typeof options.linesBefore !== 'number') options.linesBefore = 3
|
||||
if (typeof options.linesAfter !== 'number') options.linesAfter = 2
|
||||
|
||||
const re = /\r?\n|\r|\0/g
|
||||
const lineStarts = [0]
|
||||
const lineEnds = []
|
||||
let match
|
||||
let foundLineNo = -1
|
||||
|
||||
while ((match = re.exec(mark.buffer))) {
|
||||
lineEnds.push(match.index)
|
||||
lineStarts.push(match.index + match[0].length)
|
||||
|
||||
if (mark.position <= match.index && foundLineNo < 0) {
|
||||
foundLineNo = lineStarts.length - 2
|
||||
}
|
||||
}
|
||||
|
||||
if (foundLineNo < 0) foundLineNo = lineStarts.length - 1
|
||||
|
||||
let result = ''
|
||||
const lineNoLength = Math.min(mark.line + options.linesAfter, lineEnds.length).toString().length
|
||||
const maxLineLength = options.maxLength - (options.indent + lineNoLength + 3)
|
||||
|
||||
for (let i = 1; i <= options.linesBefore; i++) {
|
||||
if (foundLineNo - i < 0) break
|
||||
const line = getLine(
|
||||
mark.buffer,
|
||||
lineStarts[foundLineNo - i],
|
||||
lineEnds[foundLineNo - i],
|
||||
mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo - i]),
|
||||
maxLineLength
|
||||
)
|
||||
result = common.repeat(' ', options.indent) + padStart((mark.line - i + 1).toString(), lineNoLength) +
|
||||
' | ' + line.str + '\n' + result
|
||||
}
|
||||
|
||||
const line = getLine(mark.buffer, lineStarts[foundLineNo], lineEnds[foundLineNo], mark.position, maxLineLength)
|
||||
result += common.repeat(' ', options.indent) + padStart((mark.line + 1).toString(), lineNoLength) +
|
||||
' | ' + line.str + '\n'
|
||||
result += common.repeat('-', options.indent + lineNoLength + 3 + line.pos) + '^' + '\n'
|
||||
|
||||
for (let i = 1; i <= options.linesAfter; i++) {
|
||||
if (foundLineNo + i >= lineEnds.length) break
|
||||
const line = getLine(
|
||||
mark.buffer,
|
||||
lineStarts[foundLineNo + i],
|
||||
lineEnds[foundLineNo + i],
|
||||
mark.position - (lineStarts[foundLineNo] - lineStarts[foundLineNo + i]),
|
||||
maxLineLength
|
||||
)
|
||||
result += common.repeat(' ', options.indent) + padStart((mark.line + i + 1).toString(), lineNoLength) +
|
||||
' | ' + line.str + '\n'
|
||||
}
|
||||
|
||||
return result.replace(/\n$/, '')
|
||||
}
|
||||
|
||||
module.exports = makeSnippet
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
'use strict'
|
||||
|
||||
const YAMLException = require('./exception')
|
||||
|
||||
const TYPE_CONSTRUCTOR_OPTIONS = [
|
||||
'kind',
|
||||
'multi',
|
||||
'resolve',
|
||||
'construct',
|
||||
'instanceOf',
|
||||
'predicate',
|
||||
'represent',
|
||||
'representName',
|
||||
'defaultStyle',
|
||||
'styleAliases'
|
||||
]
|
||||
|
||||
const YAML_NODE_KINDS = [
|
||||
'scalar',
|
||||
'sequence',
|
||||
'mapping'
|
||||
]
|
||||
|
||||
function compileStyleAliases (map) {
|
||||
const result = {}
|
||||
|
||||
if (map !== null) {
|
||||
Object.keys(map).forEach(function (style) {
|
||||
map[style].forEach(function (alias) {
|
||||
result[String(alias)] = style
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function Type (tag, options) {
|
||||
options = options || {}
|
||||
|
||||
Object.keys(options).forEach(function (name) {
|
||||
if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) {
|
||||
throw new YAMLException('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.')
|
||||
}
|
||||
})
|
||||
|
||||
// TODO: Add tag format check.
|
||||
this.options = options // keep original options in case user wants to extend this type later
|
||||
this.tag = tag
|
||||
this.kind = options['kind'] || null
|
||||
this.resolve = options['resolve'] || function () { return true }
|
||||
this.construct = options['construct'] || function (data) { return data }
|
||||
this.instanceOf = options['instanceOf'] || null
|
||||
this.predicate = options['predicate'] || null
|
||||
this.represent = options['represent'] || null
|
||||
this.representName = options['representName'] || null
|
||||
this.defaultStyle = options['defaultStyle'] || null
|
||||
this.multi = options['multi'] || false
|
||||
this.styleAliases = compileStyleAliases(options['styleAliases'] || null)
|
||||
|
||||
if (YAML_NODE_KINDS.indexOf(this.kind) === -1) {
|
||||
throw new YAMLException('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Type
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
// [ 64, 65, 66 ] -> [ padding, CR, LF ]
|
||||
const BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r'
|
||||
|
||||
function resolveYamlBinary (data) {
|
||||
if (data === null) return false
|
||||
|
||||
let bitlen = 0
|
||||
const max = data.length
|
||||
const map = BASE64_MAP
|
||||
|
||||
// Convert one by one.
|
||||
for (let idx = 0; idx < max; idx++) {
|
||||
const code = map.indexOf(data.charAt(idx))
|
||||
|
||||
// Skip CR/LF
|
||||
if (code > 64) continue
|
||||
|
||||
// Fail on illegal characters
|
||||
if (code < 0) return false
|
||||
|
||||
bitlen += 6
|
||||
}
|
||||
|
||||
// If there are any bits left, source was corrupted
|
||||
return (bitlen % 8) === 0
|
||||
}
|
||||
|
||||
function constructYamlBinary (data) {
|
||||
const input = data.replace(/[\r\n=]/g, '') // remove CR/LF & padding to simplify scan
|
||||
const max = input.length
|
||||
const map = BASE64_MAP
|
||||
let bits = 0
|
||||
const result = []
|
||||
|
||||
// Collect by 6*4 bits (3 bytes)
|
||||
|
||||
for (let idx = 0; idx < max; idx++) {
|
||||
if ((idx % 4 === 0) && idx) {
|
||||
result.push((bits >> 16) & 0xFF)
|
||||
result.push((bits >> 8) & 0xFF)
|
||||
result.push(bits & 0xFF)
|
||||
}
|
||||
|
||||
bits = (bits << 6) | map.indexOf(input.charAt(idx))
|
||||
}
|
||||
|
||||
// Dump tail
|
||||
|
||||
const tailbits = (max % 4) * 6
|
||||
|
||||
if (tailbits === 0) {
|
||||
result.push((bits >> 16) & 0xFF)
|
||||
result.push((bits >> 8) & 0xFF)
|
||||
result.push(bits & 0xFF)
|
||||
} else if (tailbits === 18) {
|
||||
result.push((bits >> 10) & 0xFF)
|
||||
result.push((bits >> 2) & 0xFF)
|
||||
} else if (tailbits === 12) {
|
||||
result.push((bits >> 4) & 0xFF)
|
||||
}
|
||||
|
||||
return new Uint8Array(result)
|
||||
}
|
||||
|
||||
function representYamlBinary (object /*, style */) {
|
||||
let result = ''
|
||||
let bits = 0
|
||||
const max = object.length
|
||||
const map = BASE64_MAP
|
||||
|
||||
// Convert every three bytes to 4 ASCII characters.
|
||||
|
||||
for (let idx = 0; idx < max; idx++) {
|
||||
if ((idx % 3 === 0) && idx) {
|
||||
result += map[(bits >> 18) & 0x3F]
|
||||
result += map[(bits >> 12) & 0x3F]
|
||||
result += map[(bits >> 6) & 0x3F]
|
||||
result += map[bits & 0x3F]
|
||||
}
|
||||
|
||||
bits = (bits << 8) + object[idx]
|
||||
}
|
||||
|
||||
// Dump tail
|
||||
|
||||
const tail = max % 3
|
||||
|
||||
if (tail === 0) {
|
||||
result += map[(bits >> 18) & 0x3F]
|
||||
result += map[(bits >> 12) & 0x3F]
|
||||
result += map[(bits >> 6) & 0x3F]
|
||||
result += map[bits & 0x3F]
|
||||
} else if (tail === 2) {
|
||||
result += map[(bits >> 10) & 0x3F]
|
||||
result += map[(bits >> 4) & 0x3F]
|
||||
result += map[(bits << 2) & 0x3F]
|
||||
result += map[64]
|
||||
} else if (tail === 1) {
|
||||
result += map[(bits >> 2) & 0x3F]
|
||||
result += map[(bits << 4) & 0x3F]
|
||||
result += map[64]
|
||||
result += map[64]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function isBinary (obj) {
|
||||
return Object.prototype.toString.call(obj) === '[object Uint8Array]'
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:binary', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlBinary,
|
||||
construct: constructYamlBinary,
|
||||
predicate: isBinary,
|
||||
represent: representYamlBinary
|
||||
})
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
function resolveYamlBoolean (data) {
|
||||
if (data === null) return false
|
||||
|
||||
const max = data.length
|
||||
|
||||
return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) ||
|
||||
(max === 5 && (data === 'false' || data === 'False' || data === 'FALSE'))
|
||||
}
|
||||
|
||||
function constructYamlBoolean (data) {
|
||||
return data === 'true' ||
|
||||
data === 'True' ||
|
||||
data === 'TRUE'
|
||||
}
|
||||
|
||||
function isBoolean (object) {
|
||||
return Object.prototype.toString.call(object) === '[object Boolean]'
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:bool', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlBoolean,
|
||||
construct: constructYamlBoolean,
|
||||
predicate: isBoolean,
|
||||
represent: {
|
||||
lowercase: function (object) { return object ? 'true' : 'false' },
|
||||
uppercase: function (object) { return object ? 'TRUE' : 'FALSE' },
|
||||
camelcase: function (object) { return object ? 'True' : 'False' }
|
||||
},
|
||||
defaultStyle: 'lowercase'
|
||||
})
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
'use strict'
|
||||
|
||||
const common = require('../common')
|
||||
const Type = require('../type')
|
||||
|
||||
const YAML_FLOAT_PATTERN = new RegExp(
|
||||
// 2.5e4, 2.5 and integers
|
||||
'^(?:[-+]?(?:[0-9]+)(?:\\.[0-9]*)?(?:[eE][-+]?[0-9]+)?' +
|
||||
// .2e4, .2
|
||||
// special case, seems not from spec
|
||||
'|\\.[0-9]+(?:[eE][-+]?[0-9]+)?' +
|
||||
// .inf
|
||||
'|[-+]?\\.(?:inf|Inf|INF)' +
|
||||
// .nan
|
||||
'|\\.(?:nan|NaN|NAN))$')
|
||||
|
||||
const YAML_FLOAT_SPECIAL_PATTERN = new RegExp(
|
||||
'^(?:' +
|
||||
// .inf
|
||||
'[-+]?\\.(?:inf|Inf|INF)' +
|
||||
// .nan
|
||||
'|\\.(?:nan|NaN|NAN))$')
|
||||
|
||||
function resolveYamlFloat (data) {
|
||||
if (data === null) return false
|
||||
|
||||
if (!YAML_FLOAT_PATTERN.test(data)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (Number.isFinite(parseFloat(data, 10))) {
|
||||
return true
|
||||
}
|
||||
|
||||
return YAML_FLOAT_SPECIAL_PATTERN.test(data)
|
||||
}
|
||||
|
||||
function constructYamlFloat (data) {
|
||||
let value = data.toLowerCase()
|
||||
const sign = value[0] === '-' ? -1 : 1
|
||||
|
||||
if ('+-'.indexOf(value[0]) >= 0) {
|
||||
value = value.slice(1)
|
||||
}
|
||||
|
||||
if (value === '.inf') {
|
||||
return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY
|
||||
} else if (value === '.nan') {
|
||||
return NaN
|
||||
}
|
||||
return sign * parseFloat(value, 10)
|
||||
}
|
||||
|
||||
const SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/
|
||||
|
||||
function representYamlFloat (object, style) {
|
||||
if (isNaN(object)) {
|
||||
switch (style) {
|
||||
case 'lowercase': return '.nan'
|
||||
case 'uppercase': return '.NAN'
|
||||
case 'camelcase': return '.NaN'
|
||||
}
|
||||
} else if (Number.POSITIVE_INFINITY === object) {
|
||||
switch (style) {
|
||||
case 'lowercase': return '.inf'
|
||||
case 'uppercase': return '.INF'
|
||||
case 'camelcase': return '.Inf'
|
||||
}
|
||||
} else if (Number.NEGATIVE_INFINITY === object) {
|
||||
switch (style) {
|
||||
case 'lowercase': return '-.inf'
|
||||
case 'uppercase': return '-.INF'
|
||||
case 'camelcase': return '-.Inf'
|
||||
}
|
||||
} else if (common.isNegativeZero(object)) {
|
||||
return '-0.0'
|
||||
}
|
||||
|
||||
const res = object.toString(10)
|
||||
|
||||
// JS stringifier can build scientific format without dots: 5e-100,
|
||||
// while YAML requres dot: 5.e-100. Fix it with simple hack
|
||||
|
||||
return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res
|
||||
}
|
||||
|
||||
function isFloat (object) {
|
||||
return (Object.prototype.toString.call(object) === '[object Number]') &&
|
||||
(object % 1 !== 0 || common.isNegativeZero(object))
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:float', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlFloat,
|
||||
construct: constructYamlFloat,
|
||||
predicate: isFloat,
|
||||
represent: representYamlFloat,
|
||||
defaultStyle: 'lowercase'
|
||||
})
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
'use strict'
|
||||
|
||||
const common = require('../common')
|
||||
const Type = require('../type')
|
||||
|
||||
function isHexCode (c) {
|
||||
return ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */)) ||
|
||||
((c >= 0x41/* A */) && (c <= 0x46/* F */)) ||
|
||||
((c >= 0x61/* a */) && (c <= 0x66/* f */))
|
||||
}
|
||||
|
||||
function isOctCode (c) {
|
||||
return ((c >= 0x30/* 0 */) && (c <= 0x37/* 7 */))
|
||||
}
|
||||
|
||||
function isDecCode (c) {
|
||||
return ((c >= 0x30/* 0 */) && (c <= 0x39/* 9 */))
|
||||
}
|
||||
|
||||
function resolveYamlInteger (data) {
|
||||
if (data === null) return false
|
||||
|
||||
const max = data.length
|
||||
let index = 0
|
||||
let hasDigits = false
|
||||
|
||||
if (!max) return false
|
||||
|
||||
let ch = data[index]
|
||||
|
||||
// sign
|
||||
if (ch === '-' || ch === '+') {
|
||||
ch = data[++index]
|
||||
}
|
||||
|
||||
if (ch === '0') {
|
||||
// 0
|
||||
if (index + 1 === max) return true
|
||||
ch = data[++index]
|
||||
|
||||
// base 2, base 8, base 16
|
||||
|
||||
if (ch === 'b') {
|
||||
// base 2
|
||||
index++
|
||||
|
||||
for (; index < max; index++) {
|
||||
ch = data[index]
|
||||
if (ch !== '0' && ch !== '1') return false
|
||||
hasDigits = true
|
||||
}
|
||||
return hasDigits && Number.isFinite(parseYamlInteger(data))
|
||||
}
|
||||
|
||||
if (ch === 'x') {
|
||||
// base 16
|
||||
index++
|
||||
|
||||
for (; index < max; index++) {
|
||||
if (!isHexCode(data.charCodeAt(index))) return false
|
||||
hasDigits = true
|
||||
}
|
||||
return hasDigits && Number.isFinite(parseYamlInteger(data))
|
||||
}
|
||||
|
||||
if (ch === 'o') {
|
||||
// base 8
|
||||
index++
|
||||
|
||||
for (; index < max; index++) {
|
||||
if (!isOctCode(data.charCodeAt(index))) return false
|
||||
hasDigits = true
|
||||
}
|
||||
return hasDigits && Number.isFinite(parseYamlInteger(data))
|
||||
}
|
||||
}
|
||||
|
||||
// base 10 (except 0)
|
||||
|
||||
for (; index < max; index++) {
|
||||
if (!isDecCode(data.charCodeAt(index))) {
|
||||
return false
|
||||
}
|
||||
hasDigits = true
|
||||
}
|
||||
|
||||
if (!hasDigits) return false
|
||||
|
||||
return Number.isFinite(parseYamlInteger(data))
|
||||
}
|
||||
|
||||
function parseYamlInteger (data) {
|
||||
let value = data
|
||||
let sign = 1
|
||||
|
||||
let ch = value[0]
|
||||
|
||||
if (ch === '-' || ch === '+') {
|
||||
if (ch === '-') sign = -1
|
||||
value = value.slice(1)
|
||||
ch = value[0]
|
||||
}
|
||||
|
||||
if (value === '0') return 0
|
||||
|
||||
if (ch === '0') {
|
||||
if (value[1] === 'b') return sign * parseInt(value.slice(2), 2)
|
||||
if (value[1] === 'x') return sign * parseInt(value.slice(2), 16)
|
||||
if (value[1] === 'o') return sign * parseInt(value.slice(2), 8)
|
||||
}
|
||||
|
||||
return sign * parseInt(value, 10)
|
||||
}
|
||||
|
||||
function constructYamlInteger (data) {
|
||||
return parseYamlInteger(data)
|
||||
}
|
||||
|
||||
function isInteger (object) {
|
||||
return (Object.prototype.toString.call(object)) === '[object Number]' &&
|
||||
(object % 1 === 0 && !common.isNegativeZero(object))
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:int', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlInteger,
|
||||
construct: constructYamlInteger,
|
||||
predicate: isInteger,
|
||||
represent: {
|
||||
binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1) },
|
||||
octal: function (obj) { return obj >= 0 ? '0o' + obj.toString(8) : '-0o' + obj.toString(8).slice(1) },
|
||||
decimal: function (obj) { return obj.toString(10) },
|
||||
hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1) }
|
||||
},
|
||||
defaultStyle: 'decimal',
|
||||
styleAliases: {
|
||||
binary: [2, 'bin'],
|
||||
octal: [8, 'oct'],
|
||||
decimal: [10, 'dec'],
|
||||
hexadecimal: [16, 'hex']
|
||||
}
|
||||
})
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:map', {
|
||||
kind: 'mapping',
|
||||
construct: function (data) { return data !== null ? data : {} }
|
||||
})
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
function resolveYamlMerge (data) {
|
||||
return data === '<<' || data === null
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:merge', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlMerge
|
||||
})
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
function resolveYamlNull (data) {
|
||||
if (data === null) return true
|
||||
|
||||
const max = data.length
|
||||
|
||||
return (max === 1 && data === '~') ||
|
||||
(max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'))
|
||||
}
|
||||
|
||||
function constructYamlNull () {
|
||||
return null
|
||||
}
|
||||
|
||||
function isNull (object) {
|
||||
return object === null
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:null', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlNull,
|
||||
construct: constructYamlNull,
|
||||
predicate: isNull,
|
||||
represent: {
|
||||
canonical: function () { return '~' },
|
||||
lowercase: function () { return 'null' },
|
||||
uppercase: function () { return 'NULL' },
|
||||
camelcase: function () { return 'Null' },
|
||||
empty: function () { return '' }
|
||||
},
|
||||
defaultStyle: 'lowercase'
|
||||
})
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
const _hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
const _toString = Object.prototype.toString
|
||||
|
||||
function resolveYamlOmap (data) {
|
||||
if (data === null) return true
|
||||
|
||||
const objectKeys = []
|
||||
const object = data
|
||||
|
||||
for (let index = 0, length = object.length; index < length; index += 1) {
|
||||
const pair = object[index]
|
||||
let pairHasKey = false
|
||||
|
||||
if (_toString.call(pair) !== '[object Object]') return false
|
||||
|
||||
let pairKey
|
||||
for (pairKey in pair) {
|
||||
if (_hasOwnProperty.call(pair, pairKey)) {
|
||||
if (!pairHasKey) pairHasKey = true
|
||||
else return false
|
||||
}
|
||||
}
|
||||
|
||||
if (!pairHasKey) return false
|
||||
|
||||
if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey)
|
||||
else return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function constructYamlOmap (data) {
|
||||
return data !== null ? data : []
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:omap', {
|
||||
kind: 'sequence',
|
||||
resolve: resolveYamlOmap,
|
||||
construct: constructYamlOmap
|
||||
})
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
const _toString = Object.prototype.toString
|
||||
|
||||
function resolveYamlPairs (data) {
|
||||
if (data === null) return true
|
||||
|
||||
const object = data
|
||||
|
||||
const result = new Array(object.length)
|
||||
|
||||
for (let index = 0, length = object.length; index < length; index += 1) {
|
||||
const pair = object[index]
|
||||
|
||||
if (_toString.call(pair) !== '[object Object]') return false
|
||||
|
||||
const keys = Object.keys(pair)
|
||||
|
||||
if (keys.length !== 1) return false
|
||||
|
||||
result[index] = [keys[0], pair[keys[0]]]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function constructYamlPairs (data) {
|
||||
if (data === null) return []
|
||||
|
||||
const object = data
|
||||
const result = new Array(object.length)
|
||||
|
||||
for (let index = 0, length = object.length; index < length; index += 1) {
|
||||
const pair = object[index]
|
||||
|
||||
const keys = Object.keys(pair)
|
||||
|
||||
result[index] = [keys[0], pair[keys[0]]]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:pairs', {
|
||||
kind: 'sequence',
|
||||
resolve: resolveYamlPairs,
|
||||
construct: constructYamlPairs
|
||||
})
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:seq', {
|
||||
kind: 'sequence',
|
||||
construct: function (data) { return data !== null ? data : [] }
|
||||
})
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
const _hasOwnProperty = Object.prototype.hasOwnProperty
|
||||
|
||||
function resolveYamlSet (data) {
|
||||
if (data === null) return true
|
||||
|
||||
const object = data
|
||||
|
||||
for (const key in object) {
|
||||
if (_hasOwnProperty.call(object, key)) {
|
||||
if (object[key] !== null) return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function constructYamlSet (data) {
|
||||
return data !== null ? data : {}
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:set', {
|
||||
kind: 'mapping',
|
||||
resolve: resolveYamlSet,
|
||||
construct: constructYamlSet
|
||||
})
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:str', {
|
||||
kind: 'scalar',
|
||||
construct: function (data) { return data !== null ? data : '' }
|
||||
})
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
'use strict'
|
||||
|
||||
const Type = require('../type')
|
||||
|
||||
const YAML_DATE_REGEXP = new RegExp(
|
||||
'^([0-9][0-9][0-9][0-9])' + // [1] year
|
||||
'-([0-9][0-9])' + // [2] month
|
||||
'-([0-9][0-9])$') // [3] day
|
||||
|
||||
const YAML_TIMESTAMP_REGEXP = new RegExp(
|
||||
'^([0-9][0-9][0-9][0-9])' + // [1] year
|
||||
'-([0-9][0-9]?)' + // [2] month
|
||||
'-([0-9][0-9]?)' + // [3] day
|
||||
'(?:[Tt]|[ \\t]+)' + // ...
|
||||
'([0-9][0-9]?)' + // [4] hour
|
||||
':([0-9][0-9])' + // [5] minute
|
||||
':([0-9][0-9])' + // [6] second
|
||||
'(?:\\.([0-9]*))?' + // [7] fraction
|
||||
'(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tzHour
|
||||
'(?::([0-9][0-9]))?))?$') // [11] tzMinute
|
||||
|
||||
function resolveYamlTimestamp (data) {
|
||||
if (data === null) return false
|
||||
if (YAML_DATE_REGEXP.exec(data) !== null) return true
|
||||
if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true
|
||||
return false
|
||||
}
|
||||
|
||||
function constructYamlTimestamp (data) {
|
||||
let fraction = 0
|
||||
let delta = null
|
||||
|
||||
let match = YAML_DATE_REGEXP.exec(data)
|
||||
if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data)
|
||||
|
||||
if (match === null) throw new Error('Date resolve error')
|
||||
|
||||
// match: [1] year [2] month [3] day
|
||||
|
||||
const year = +(match[1])
|
||||
const month = +(match[2]) - 1 // JS month starts with 0
|
||||
const day = +(match[3])
|
||||
|
||||
if (!match[4]) { // no hour
|
||||
return new Date(Date.UTC(year, month, day))
|
||||
}
|
||||
|
||||
// match: [4] hour [5] minute [6] second [7] fraction
|
||||
|
||||
const hour = +(match[4])
|
||||
const minute = +(match[5])
|
||||
const second = +(match[6])
|
||||
|
||||
if (match[7]) {
|
||||
fraction = match[7].slice(0, 3)
|
||||
while (fraction.length < 3) { // milli-seconds
|
||||
fraction += '0'
|
||||
}
|
||||
fraction = +fraction
|
||||
}
|
||||
|
||||
// match: [8] tz [9] tz_sign [10] tzHour [11] tzMinute
|
||||
|
||||
if (match[9]) {
|
||||
const tzHour = +(match[10])
|
||||
const tzMinute = +(match[11] || 0)
|
||||
delta = (tzHour * 60 + tzMinute) * 60000 // delta in mili-seconds
|
||||
if (match[9] === '-') delta = -delta
|
||||
}
|
||||
|
||||
const date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction))
|
||||
|
||||
if (delta) date.setTime(date.getTime() - delta)
|
||||
|
||||
return date
|
||||
}
|
||||
|
||||
function representYamlTimestamp (object /*, style */) {
|
||||
return object.toISOString()
|
||||
}
|
||||
|
||||
module.exports = new Type('tag:yaml.org,2002:timestamp', {
|
||||
kind: 'scalar',
|
||||
resolve: resolveYamlTimestamp,
|
||||
construct: constructYamlTimestamp,
|
||||
instanceOf: Date,
|
||||
represent: representYamlTimestamp
|
||||
})
|
||||
Reference in New Issue
Block a user