1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-08 23:28:18 -05:00

feat: Add std/encoding/yaml module (#3361)

This commit is contained in:
Lilian Saget-Lethias 2019-11-18 15:39:32 +01:00 committed by Ry Dahl
parent a2f5bccad7
commit 5671d38d8f
41 changed files with 4597 additions and 0 deletions

View file

@ -219,3 +219,24 @@ const obj = {
};
const tomlString = stringify(obj);
```
## YAML
YAML parser / dumper for Deno
Heavily inspired from [js-yaml]
### Example
See [`./yaml/example`](./yaml/example) folder and [js-yaml] repository.
### :warning: Limitations
- `binary` type is currently not stable
- `function`, `regexp`, and `undefined` type are currently not supported
# Basic usage
TBD
[js-yaml]: https://github.com/nodeca/js-yaml

8
std/encoding/yaml.ts Normal file
View file

@ -0,0 +1,8 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
export * from "./yaml/parse.ts";
export * from "./yaml/stringify.ts";
export * from "./yaml/schema/mod.ts";

View file

@ -0,0 +1,899 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
/* eslint-disable max-len */
import { YAMLError } from "../error.ts";
import { RepresentFn, StyleVariant, Type } from "../type.ts";
import * as common from "../utils.ts";
import { DumperState, DumperStateOptions } from "./dumper_state.ts";
type Any = common.Any;
type ArrayObject<T = Any> = common.ArrayObject<T>;
const _toString = Object.prototype.toString;
const _hasOwnProperty = Object.prototype.hasOwnProperty;
const CHAR_TAB = 0x09; /* Tab */
const CHAR_LINE_FEED = 0x0a; /* LF */
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_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: { [char: number]: string } = {};
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"
];
function encodeHex(character: number): string {
const string = character.toString(16).toUpperCase();
let handle: string;
let length: number;
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 YAMLError(
"code point within a string may not be greater than 0xFFFFFFFF"
);
}
return `\\${handle}${common.repeat("0", length - string.length)}${string}`;
}
// Indents every line in a string. Empty lines (\n only) are not indented.
function indentString(string: string, spaces: number): string {
const ind = common.repeat(" ", spaces),
length = string.length;
let position = 0,
next = -1,
result = "",
line: string;
while (position < length) {
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: DumperState, level: number): string {
return `\n${common.repeat(" ", state.indent * level)}`;
}
function testImplicitResolving(state: DumperState, str: string): boolean {
let type: Type;
for (
let index = 0, length = state.implicitTypes.length;
index < length;
index += 1
) {
type = state.implicitTypes[index];
if (type.resolve(str)) {
return true;
}
}
return false;
}
// [33] s-white ::= s-space | s-tab
function isWhitespace(c: number): boolean {
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 isnt mandatory"
// Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029.
function isPrintable(c: number): boolean {
return (
(0x00020 <= c && c <= 0x00007e) ||
(0x000a1 <= c && c <= 0x00d7ff && c !== 0x2028 && c !== 0x2029) ||
(0x0e000 <= c && c <= 0x00fffd && c !== 0xfeff) /* BOM */ ||
(0x10000 <= c && c <= 0x10ffff)
);
}
// Simplified test for values allowed after the first character in plain style.
function isPlainSafe(c: number): boolean {
// Uses a subset of nb-char - c-flow-indicator - ":" - "#"
// where nb-char ::= c-printable - b-char - c-byte-order-mark.
return (
isPrintable(c) &&
c !== 0xfeff &&
// - 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 &&
// - ":" - "#"
c !== CHAR_COLON &&
c !== CHAR_SHARP
);
}
// Simplified test for values allowed as the first character in plain style.
function isPlainSafeFirst(c: number): boolean {
// Uses a subset of ns-char - c-indicator
// where ns-char = nb-char - s-white.
return (
isPrintable(c) &&
c !== 0xfeff &&
!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_GREATER_THAN &&
c !== CHAR_SINGLE_QUOTE &&
c !== CHAR_DOUBLE_QUOTE &&
// | “%” | “@” | “`”)
c !== CHAR_PERCENT &&
c !== CHAR_COMMERCIAL_AT &&
c !== CHAR_GRAVE_ACCENT
);
}
// Determines whether block indentation indicator is required.
function needIndentIndicator(string: string): boolean {
const leadingSpaceRe = /^\n* /;
return leadingSpaceRe.test(string);
}
const STYLE_PLAIN = 1,
STYLE_SINGLE = 2,
STYLE_LITERAL = 3,
STYLE_FOLDED = 4,
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: string,
singleLineOnly: boolean,
indentPerLevel: number,
lineWidth: number,
testAmbiguousType: (...args: Any[]) => Any
): number {
const shouldTrackWidth = lineWidth !== -1;
let hasLineBreak = false,
hasFoldableLine = false, // only checked if shouldTrackWidth
previousLineBreak = -1, // count the first line correctly
plain =
isPlainSafeFirst(string.charCodeAt(0)) &&
!isWhitespace(string.charCodeAt(string.length - 1));
let char: number, i: number;
if (singleLineOnly) {
// Case: no block styles.
// Check for disallowed characters to rule out plain and single.
for (i = 0; i < string.length; i++) {
char = string.charCodeAt(i);
if (!isPrintable(char)) {
return STYLE_DOUBLE;
}
plain = plain && isPlainSafe(char);
}
} else {
// Case: block styles permitted.
for (i = 0; i < string.length; i++) {
char = string.charCodeAt(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);
}
// 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.
return plain && !testAmbiguousType(string) ? STYLE_PLAIN : 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.
return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL;
}
// 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: string, width: number): string {
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,
end,
curr = 0,
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.
// tslint:disable-next-line:no-conditional-assignment
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
}
// (See the note for writeScalar.)
function dropEndingNewline(string: string): 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: string, width: number): string {
// 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 = ((): string => {
let nextLF = string.indexOf("\n");
nextLF = nextLF !== -1 ? nextLF : string.length;
lineRe.lastIndex = nextLF;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
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;
// tslint:disable-next-line:no-conditional-assignment
while ((match = lineRe.exec(string))) {
const prefix = match[1],
line = match[2];
moreIndented = line[0] === " ";
result +=
prefix +
(!prevMoreIndented && !moreIndented && line !== "" ? "\n" : "") +
// eslint-disable-next-line @typescript-eslint/no-use-before-define
foldLine(line, width);
prevMoreIndented = moreIndented;
}
return result;
}
// Escapes a double-quoted string.
function escapeString(string: string): string {
let result = "";
let char, nextChar;
let escapeSeq;
for (let i = 0; i < string.length; i++) {
char = string.charCodeAt(i);
// Check for surrogate pairs (reference Unicode 3.0 section "3.7 Surrogates").
if (char >= 0xd800 && char <= 0xdbff /* high surrogate */) {
nextChar = string.charCodeAt(i + 1);
if (nextChar >= 0xdc00 && nextChar <= 0xdfff /* low surrogate */) {
// Combine the surrogate pair and store it escaped.
result += encodeHex(
(char - 0xd800) * 0x400 + nextChar - 0xdc00 + 0x10000
);
// Advance index one extra since we already used that char here.
i++;
continue;
}
}
escapeSeq = ESCAPE_SEQUENCES[char];
result +=
!escapeSeq && isPrintable(char)
? string[i]
: escapeSeq || encodeHex(char);
}
return result;
}
// Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.
function blockHeader(string: string, indentPerLevel: number): string {
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`;
}
// 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: DumperState,
string: string,
level: number,
iskey: boolean
): void {
state.dump = ((): string => {
if (string.length === 0) {
return "''";
}
if (
!state.noCompatMode &&
DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1
) {
return `'${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(str: string): boolean {
return testImplicitResolving(state, str);
}
switch (
chooseScalarStyle(
string,
singleLineOnly,
state.indent,
lineWidth,
testAmbiguity
)
) {
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)}"`;
default:
throw new YAMLError("impossible error: invalid scalar style");
}
})();
}
function writeFlowSequence(
state: DumperState,
level: number,
object: Any
): void {
let _result = "";
const _tag = state.tag;
for (let index = 0, length = object.length; index < length; index += 1) {
// Write only valid elements.
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (writeNode(state, level, object[index], false, false)) {
if (index !== 0) _result += `,${!state.condenseFlow ? " " : ""}`;
_result += state.dump;
}
}
state.tag = _tag;
state.dump = `[${_result}]`;
}
function writeBlockSequence(
state: DumperState,
level: number,
object: Any,
compact = false
): void {
let _result = "";
const _tag = state.tag;
for (let index = 0, length = object.length; index < length; index += 1) {
// Write only valid elements.
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (writeNode(state, level + 1, object[index], true, true)) {
if (!compact || index !== 0) {
_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: DumperState,
level: number,
object: Any
): void {
let _result = "";
const _tag = state.tag,
objectKeyList = Object.keys(object);
let pairBuffer: string, objectKey: string, objectValue: Any;
for (
let index = 0, length = objectKeyList.length;
index < length;
index += 1
) {
pairBuffer = state.condenseFlow ? '"' : "";
if (index !== 0) pairBuffer += ", ";
objectKey = objectKeyList[index];
objectValue = object[objectKey];
// eslint-disable-next-line @typescript-eslint/no-use-before-define
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 ? "" : " "
}`;
// eslint-disable-next-line @typescript-eslint/no-use-before-define
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: DumperState,
level: number,
object: Any,
compact = false
): void {
const _tag = state.tag,
objectKeyList = Object.keys(object);
let _result = "";
// 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 YAMLError("sortKeys must be a boolean or a function");
}
let pairBuffer = "",
objectKey: string,
objectValue: Any,
explicitPair: boolean;
for (
let index = 0, length = objectKeyList.length;
index < length;
index += 1
) {
pairBuffer = "";
if (!compact || index !== 0) {
pairBuffer += generateNextLine(state, level);
}
objectKey = objectKeyList[index];
objectValue = object[objectKey];
// eslint-disable-next-line @typescript-eslint/no-use-before-define
if (!writeNode(state, level + 1, objectKey, true, true, true)) {
continue; // Skip this pair because of invalid key.
}
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);
}
// eslint-disable-next-line @typescript-eslint/no-use-before-define
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: DumperState,
object: Any,
explicit = false
): boolean {
const typeList = explicit ? state.explicitTypes : state.implicitTypes;
let type: Type;
let style: StyleVariant;
let _result: string;
for (let index = 0, length = typeList.length; index < length; index += 1) {
type = typeList[index];
if (
(type.instanceOf || type.predicate) &&
(!type.instanceOf ||
(typeof object === "object" && object instanceof type.instanceOf)) &&
(!type.predicate || type.predicate(object))
) {
state.tag = explicit ? type.tag : "?";
if (type.represent) {
style = state.styleMap[type.tag] || type.defaultStyle;
if (_toString.call(type.represent) === "[object Function]") {
_result = (type.represent as RepresentFn)(object, style);
} else if (_hasOwnProperty.call(type.represent, style)) {
_result = (type.represent as ArrayObject<RepresentFn>)[style](
object,
style
);
} else {
throw new YAMLError(
`!<${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: DumperState,
level: number,
object: Any,
block: boolean,
compact: boolean,
iskey = false
): boolean {
state.tag = null;
state.dump = object;
if (!detectType(state, object, false)) {
detectType(state, object, true);
}
const type = _toString.call(state.dump);
if (block) {
block = state.flowLevel < 0 || state.flowLevel > level;
}
const objectOrArray = type === "[object Object]" || type === "[object Array]";
let duplicateIndex = -1;
let duplicate = false;
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]") {
const arrayLevel = state.noArrayIndent && level > 0 ? level - 1 : level;
if (block && state.dump.length !== 0) {
writeBlockSequence(state, arrayLevel, state.dump, compact);
if (duplicate) {
state.dump = `&ref_${duplicateIndex}${state.dump}`;
}
} else {
writeFlowSequence(state, arrayLevel, state.dump);
if (duplicate) {
state.dump = `&ref_${duplicateIndex} ${state.dump}`;
}
}
} else if (type === "[object String]") {
if (state.tag !== "?") {
writeScalar(state, state.dump, level, iskey);
}
} else {
if (state.skipInvalid) return false;
throw new YAMLError(`unacceptable kind of an object to dump ${type}`);
}
if (state.tag !== null && state.tag !== "?") {
state.dump = `!<${state.tag}> ${state.dump}`;
}
}
return true;
}
function inspectNode(
object: Any,
objects: Any[],
duplicatesIndexes: number[]
): void {
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 idx = 0, length = object.length; idx < length; idx += 1) {
inspectNode(object[idx], objects, duplicatesIndexes);
}
} else {
const objectKeyList = Object.keys(object);
for (
let idx = 0, length = objectKeyList.length;
idx < length;
idx += 1
) {
inspectNode(object[objectKeyList[idx]], objects, duplicatesIndexes);
}
}
}
}
}
function getDuplicateReferences(object: object, state: DumperState): void {
const objects: Any[] = [],
duplicatesIndexes: number[] = [];
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);
}
export function dump(input: Any, options?: DumperStateOptions): string {
options = options || {};
const state = new DumperState(options);
if (!state.noRefs) getDuplicateReferences(input, state);
if (writeNode(state, 0, input, true, true)) return `${state.dump}\n`;
return "";
}

View file

@ -0,0 +1,138 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Schema, SchemaDefinition } from "../schema.ts";
import { State } from "../state.ts";
import { StyleVariant, Type } from "../type.ts";
import { ArrayObject, Any } from "../utils.ts";
const _hasOwnProperty = Object.prototype.hasOwnProperty;
function compileStyleMap(
schema: Schema,
map?: ArrayObject<StyleVariant> | null
): ArrayObject<StyleVariant> {
if (typeof map === "undefined" || map === null) return {};
let type: Type;
const result: ArrayObject<StyleVariant> = {};
const keys = Object.keys(map);
let tag: string, style: StyleVariant;
for (let index = 0, length = keys.length; index < length; index += 1) {
tag = keys[index];
style = String(map[tag]) as StyleVariant;
if (tag.slice(0, 2) === "!!") {
tag = `tag:yaml.org,2002:${tag.slice(2)}`;
}
type = schema.compiledTypeMap.fallback[tag];
if (
type &&
typeof type.styleAliases !== "undefined" &&
_hasOwnProperty.call(type.styleAliases, style)
) {
style = type.styleAliases[style];
}
result[tag] = style;
}
return result;
}
export interface DumperStateOptions {
/** indentation width to use (in spaces). */
indent?: number;
/** when true, will not add an indentation level to array elements */
noArrayIndent?: boolean;
/**
* do not throw on invalid types (like function in the safe schema)
* and skip pairs and single values with such types.
*/
skipInvalid?: boolean;
/**
* specifies level of nesting, when to switch from
* block to flow style for collections. -1 means block style everwhere
*/
flowLevel?: number;
/** Each tag may have own set of styles. - "tag" => "style" map. */
styles?: ArrayObject<StyleVariant> | null;
/** specifies a schema to use. */
schema?: SchemaDefinition;
/**
* if true, sort keys when dumping YAML.
* If a function, use the function to sort the keys. (default: false)
*/
sortKeys?: boolean | ((a: Any, b: Any) => number);
/** set max line width. (default: 80) */
lineWidth?: number;
/**
* if true, don't convert duplicate objects
* into references (default: false)
*/
noRefs?: boolean;
/**
* if true don't try to be compatible with older yaml versions.
* Currently: don't quote "yes", "no" and so on,
* as required for YAML 1.1 (default: false)
*/
noCompatMode?: boolean;
/**
* if true flow sequences will be condensed, omitting the
* space between `key: value` or `a, b`. Eg. `'[a,b]'` or `{a:{b:c}}`.
* Can be useful when using yaml for pretty URL query params
* as spaces are %-encoded. (default: false).
*/
condenseFlow?: boolean;
}
export class DumperState extends State {
public indent: number;
public noArrayIndent: boolean;
public skipInvalid: boolean;
public flowLevel: number;
public sortKeys: boolean | ((a: Any, b: Any) => number);
public lineWidth: number;
public noRefs: boolean;
public noCompatMode: boolean;
public condenseFlow: boolean;
public implicitTypes: Type[];
public explicitTypes: Type[];
public tag: string | null = null;
public result = "";
public duplicates: Any[] = [];
public usedDuplicates: Any[] = []; // changed from null to []
public styleMap: ArrayObject<StyleVariant>;
public dump: Any;
constructor({
schema,
indent = 2,
noArrayIndent = false,
skipInvalid = false,
flowLevel = -1,
styles = null,
sortKeys = false,
lineWidth = 80,
noRefs = false,
noCompatMode = false,
condenseFlow = false
}: DumperStateOptions) {
super(schema);
this.indent = Math.max(1, indent);
this.noArrayIndent = noArrayIndent;
this.skipInvalid = skipInvalid;
this.flowLevel = flowLevel;
this.styleMap = compileStyleMap(this.schema as Schema, styles);
this.sortKeys = sortKeys;
this.lineWidth = lineWidth;
this.noRefs = noRefs;
this.noCompatMode = noCompatMode;
this.condenseFlow = condenseFlow;
this.implicitTypes = (this.schema as Schema).compiledImplicit;
this.explicitTypes = (this.schema as Schema).compiledExplicit;
}
}

View file

@ -0,0 +1,22 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Mark } from "./mark.ts";
const { DenoError, ErrorKind } = Deno;
export class YAMLError extends DenoError<typeof ErrorKind.Other> {
constructor(
message = "(unknown reason)",
protected mark: Mark | string = ""
) {
super(ErrorKind.Other, `${message} ${mark}`);
this.name = this.constructor.name;
}
public toString(_compact: boolean): string {
return `${this.name}: ${this.message} ${this.mark}`;
}
}

View file

@ -0,0 +1,22 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { stringify } from "../../yaml.ts";
console.log(
stringify({
foo: {
bar: true,
test: [
"a",
"b",
{
a: false
},
{
a: false
}
]
},
test: "foobar"
})
);

View file

@ -0,0 +1,27 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { parse, stringify } from "../../yaml.ts";
const test = {
foo: {
bar: true,
test: [
"a",
"b",
{
a: false
},
{
a: false
}
]
},
test: "foobar"
};
const string = stringify(test);
if (Deno.inspect(test) === Deno.inspect(parse(string))) {
console.log("In-Out as expected.");
} else {
console.log("Someting went wrong.");
}

View file

@ -0,0 +1,19 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { parse } from "../../yaml.ts";
const result = parse(`
test: toto
foo:
bar: True
baz: 1
qux: ~
`);
console.log(result);
const expected = '{ test: "toto", foo: { bar: true, baz: 1, qux: null } }';
if (Deno.inspect(result) === expected) {
console.log("Output is as expected.");
} else {
console.error("Error during parse. Output is not as expect.", expected);
}

View file

@ -0,0 +1,23 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { parse } from "../../yaml.ts";
const { readFileSync, cwd } = Deno;
(async () => {
const yml = readFileSync(`${cwd()}/example/sample_document.yml`);
const document = new TextDecoder().decode(yml);
const obj = parse(document) as object;
console.log(obj);
let i = 0;
for (const o of Object.values(obj)) {
console.log(`======${i}`);
for (const [key, value] of Object.entries(o)) {
console.log(key, value);
}
i++;
}
})();

View file

@ -0,0 +1,197 @@
---
# Collection Types #############################################################
################################################################################
# http://yaml.org/type/map.html -----------------------------------------------#
map:
# Unordered set of key: value pairs.
Block style: !!map
Clark : Evans
Ingy : döt Net
Oren : Ben-Kiki
Flow style: !!map { Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki }
# http://yaml.org/type/omap.html ----------------------------------------------#
omap:
# Explicitly typed ordered map (dictionary).
Bestiary: !!omap
- aardvark: African pig-like ant eater. Ugly.
- anteater: South-American ant eater. Two species.
- anaconda: South-American constrictor snake. Scaly.
# Etc.
# Flow style
Numbers: !!omap [ one: 1, two: 2, three : 3 ]
# http://yaml.org/type/pairs.html ---------------------------------------------#
pairs:
# Explicitly typed pairs.
Block tasks: !!pairs
- meeting: with team.
- meeting: with boss.
- break: lunch.
- meeting: with client.
Flow tasks: !!pairs [ meeting: with team, meeting: with boss ]
# http://yaml.org/type/set.html -----------------------------------------------#
set:
# Explicitly typed set.
baseball players: !!set
? Mark McGwire
? Sammy Sosa
? Ken Griffey
# Flow style
baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees }
# http://yaml.org/type/seq.html -----------------------------------------------#
seq:
# Ordered sequence of nodes
Block style: !!seq
- Mercury # Rotates - no light/dark sides.
- Venus # Deadliest. Aptly named.
- Earth # Mostly dirt.
- Mars # Seems empty.
- Jupiter # The king.
- Saturn # Pretty.
- Uranus # Where the sun hardly shines.
- Neptune # Boring. No rings.
- Pluto # You call this a planet?
Flow style: !!seq [ Mercury, Venus, Earth, Mars, # Rocks
Jupiter, Saturn, Uranus, Neptune, # Gas
Pluto ] # Overrated
# Scalar Types #################################################################
################################################################################
# http://yaml.org/type/binary.html --------------------------------------------#
binary:
canonical: !!binary "\
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5\
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+\
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC\
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs="
generic: !!binary |
R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5
OTk6enp56enmlpaWNjY6Ojo4SEhP/++f/++f/++f/++f/++f/++f/++f/++f/+
+f/++f/++f/++f/++f/++SH+Dk1hZGUgd2l0aCBHSU1QACwAAAAADAAMAAAFLC
AgjoEwnuNAFOhpEMTRiggcz4BNJHrv/zCFcLiwMWYNG84BwwEeECcgggoBADs=
description:
The binary value above is a tiny arrow encoded as a gif image.
# http://yaml.org/type/bool.html ----------------------------------------------#
bool:
- true
- True
- TRUE
- false
- False
- FALSE
# http://yaml.org/type/float.html ---------------------------------------------#
float:
canonical: 6.8523015e+5
exponential: 685.230_15e+03
fixed: 685_230.15
sexagesimal: 190:20:30.15
negative infinity: -.inf
not a number: .NaN
# http://yaml.org/type/int.html -----------------------------------------------#
int:
canonical: 685230
decimal: +685_230
octal: 02472256
hexadecimal: 0x_0A_74_AE
binary: 0b1010_0111_0100_1010_1110
sexagesimal: 190:20:30
# http://yaml.org/type/merge.html ---------------------------------------------#
merge:
- &CENTER { x: 1, y: 2 }
- &LEFT { x: 0, y: 2 }
- &BIG { r: 10 }
- &SMALL { r: 1 }
# All the following maps are equal:
- # Explicit keys
x: 1
y: 2
r: 10
label: nothing
- # Merge one map
<< : *CENTER
r: 10
label: center
- # Merge multiple maps
<< : [ *CENTER, *BIG ]
label: center/big
- # Override
<< : [ *BIG, *LEFT, *SMALL ]
x: 1
label: big/left/small
# http://yaml.org/type/null.html ----------------------------------------------#
null:
# This mapping has four keys,
# one has a value.
empty:
canonical: ~
english: null
~: null key
# This sequence has five
# entries, two have values.
sparse:
- ~
- 2nd entry
-
- 4th entry
- Null
# http://yaml.org/type/str.html -----------------------------------------------#
string: abcd
# http://yaml.org/type/timestamp.html -----------------------------------------#
timestamp:
canonical: 2001-12-15T02:59:43.1Z
valid iso8601: 2001-12-14t21:59:43.10-05:00
space separated: 2001-12-14 21:59:43.10 -5
no time zone (Z): 2001-12-15 2:59:43.10
date (00:00:00Z): 2002-12-14
# JavaScript Specific Types ####################################################
################################################################################
# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/RegExp
# regexp:
# simple: !!js/regexp foobar
# modifiers: !!js/regexp /foobar/mi
# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/undefined
# undefined: !!js/undefined ~
# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function
# function: !!js/function >
# function foobar() {
# return 'Wow! JS-YAML Rocks!';
# }

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,74 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { YAMLError } from "../error.ts";
import { Schema, SchemaDefinition, TypeMap } from "../schema.ts";
import { State } from "../state.ts";
import { Type } from "../type.ts";
import { Any, ArrayObject } from "../utils.ts";
export interface LoaderStateOptions {
legacy?: boolean;
listener?: ((...args: Any[]) => void) | null;
/** string to be used as a file path in error/warning messages. */
filename?: string;
/** specifies a schema to use. */
schema?: SchemaDefinition;
/** compatibility with JSON.parse behaviour. */
json?: boolean;
/** function to call on warning messages. */
onWarning?(this: null, e?: YAMLError): void;
}
export type ResultType = [] | {} | string;
export class LoaderState extends State {
public documents: Any[] = [];
public length: number;
public lineIndent = 0;
public lineStart = 0;
public position = 0;
public line = 0;
public filename?: string;
public onWarning?: (...args: Any[]) => void;
public legacy: boolean;
public json: boolean;
public listener?: ((...args: Any[]) => void) | null;
public implicitTypes: Type[];
public typeMap: TypeMap;
public version?: string | null;
public checkLineBreaks?: boolean;
public tagMap?: ArrayObject;
public anchorMap?: ArrayObject;
public tag?: string | null;
public anchor?: string | null;
public kind?: string | null;
public result: ResultType | null = "";
constructor(
public input: string,
{
filename,
schema,
onWarning,
legacy = false,
json = false,
listener = null
}: LoaderStateOptions
) {
super(schema);
this.filename = filename;
this.onWarning = onWarning;
this.legacy = legacy;
this.json = json;
this.listener = listener;
this.implicitTypes = (this.schema as Schema).compiledImplicit;
this.typeMap = (this.schema as Schema).compiledTypeMap;
this.length = input.length;
}
}

77
std/encoding/yaml/mark.ts Normal file
View file

@ -0,0 +1,77 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { repeat } from "./utils.ts";
export class Mark {
constructor(
public name: string,
public buffer: string,
public position: number,
public line: number,
public column: number
) {}
public getSnippet(indent = 4, maxLength = 75): string | null {
if (!this.buffer) return null;
let head = "";
let start = this.position;
while (
start > 0 &&
"\x00\r\n\x85\u2028\u2029".indexOf(this.buffer.charAt(start - 1)) === -1
) {
start -= 1;
if (this.position - start > maxLength / 2 - 1) {
head = " ... ";
start += 5;
break;
}
}
let tail = "";
let end = this.position;
while (
end < this.buffer.length &&
"\x00\r\n\x85\u2028\u2029".indexOf(this.buffer.charAt(end)) === -1
) {
end += 1;
if (end - this.position > maxLength / 2 - 1) {
tail = " ... ";
end -= 5;
break;
}
}
const snippet = this.buffer.slice(start, end);
return `${repeat(" ", indent)}${head}${snippet}${tail}\n${repeat(
" ",
indent + this.position - start + head.length
)}^`;
}
public toString(compact?: boolean): string {
let snippet,
where = "";
if (this.name) {
where += `in "${this.name}" `;
}
where += `at line ${this.line + 1}, column ${this.column + 1}`;
if (!compact) {
snippet = this.getSnippet();
if (snippet) {
where += `:\n${snippet}`;
}
}
return where;
}
}

View file

@ -0,0 +1,32 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { CbFunction, load, loadAll } from "./loader/loader.ts";
import { LoaderStateOptions } from "./loader/loader_state.ts";
export type ParseOptions = LoaderStateOptions;
/**
* Parses `content` as single YAML document.
*
* Returns a JavaScript object or throws `YAMLException` on error.
* By default, does not support regexps, functions and undefined. This method is safe for untrusted data.
*
*/
export function parse(content: string, options?: ParseOptions): unknown {
return load(content, options);
}
/**
* Same as `parse()`, but understands multi-document sources.
* Applies iterator to each document if specified, or returns array of documents.
*/
export function parseAll(
content: string,
iterator?: CbFunction,
options?: ParseOptions
): unknown {
return loadAll(content, iterator, options);
}

View file

@ -0,0 +1,25 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { parse } from "./parse.ts";
import { test } from "../../testing/mod.ts";
import { assertEquals } from "../../testing/asserts.ts";
test({
name: "parsed correctly",
fn(): void {
const FIXTURE = `
test: toto
foo:
bar: True
baz: 1
qux: ~
`;
const ASSERTS = { test: "toto", foo: { bar: true, baz: 1, qux: null } };
assertEquals(parse(FIXTURE), ASSERTS);
}
});

101
std/encoding/yaml/schema.ts Normal file
View file

@ -0,0 +1,101 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { YAMLError } from "./error.ts";
import { KindType, Type } from "./type.ts";
import { ArrayObject, Any } from "./utils.ts";
function compileList(
schema: Schema,
name: "implicit" | "explicit",
result: Type[]
): Type[] {
const exclude: number[] = [];
for (const includedSchema of schema.include) {
result = compileList(includedSchema, name, result);
}
for (const currentType of schema[name]) {
for (
let previousIndex = 0;
previousIndex < result.length;
previousIndex++
) {
const previousType = result[previousIndex];
if (
previousType.tag === currentType.tag &&
previousType.kind === currentType.kind
) {
exclude.push(previousIndex);
}
}
result.push(currentType);
}
return result.filter((type, index): unknown => !exclude.includes(index));
}
export type TypeMap = { [k in KindType | "fallback"]: ArrayObject<Type> };
function compileMap(...typesList: Type[][]): TypeMap {
const result: TypeMap = {
fallback: {},
mapping: {},
scalar: {},
sequence: {}
};
for (const types of typesList) {
for (const type of types) {
if (type.kind !== null) {
result[type.kind][type.tag] = result["fallback"][type.tag] = type;
}
}
}
return result;
}
export class Schema implements SchemaDefinition {
public static SCHEMA_DEFAULT?: Schema;
public implicit: Type[];
public explicit: Type[];
public include: Schema[];
public compiledImplicit: Type[];
public compiledExplicit: Type[];
public compiledTypeMap: TypeMap;
constructor(definition: SchemaDefinition) {
this.explicit = definition.explicit || [];
this.implicit = definition.implicit || [];
this.include = definition.include || [];
for (const type of this.implicit) {
if (type.loadKind && type.loadKind !== "scalar") {
throw new YAMLError(
// eslint-disable-next-line max-len
"There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported."
);
}
}
this.compiledImplicit = compileList(this, "implicit", []);
this.compiledExplicit = compileList(this, "explicit", []);
this.compiledTypeMap = compileMap(
this.compiledImplicit,
this.compiledExplicit
);
}
public static create(): void {}
}
export interface SchemaDefinition {
implicit?: Any[];
explicit?: Type[];
include?: Schema[];
}

View file

@ -0,0 +1,13 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Schema } from "../schema.ts";
import { json } from "./json.ts";
// Standard YAML's Core schema.
// http://www.yaml.org/spec/1.2/spec.html#id2804923
export const core = new Schema({
include: [json]
});

View file

@ -0,0 +1,16 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Schema } from "../schema.ts";
import { binary, merge, omap, pairs, set, timestamp } from "../type/mod.ts";
import { core } from "./core.ts";
// JS-YAML's default schema for `safeLoad` function.
// It is not described in the YAML specification.
export const def = new Schema({
explicit: [binary, omap, pairs, set],
implicit: [timestamp, merge],
include: [core]
});

View file

@ -0,0 +1,13 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Schema } from "../schema.ts";
import { map, seq, str } from "../type/mod.ts";
// Standard YAML's Failsafe schema.
// http://www.yaml.org/spec/1.2/spec.html#id2802346
export const failsafe = new Schema({
explicit: [str, seq, map]
});

View file

@ -0,0 +1,15 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Schema } from "../schema.ts";
import { bool, float, int, nil } from "../type/mod.ts";
import { failsafe } from "./failsafe.ts";
// Standard YAML's JSON schema.
// http://www.yaml.org/spec/1.2/spec.html#id2803231
export const json = new Schema({
implicit: [nil, bool, int, float],
include: [failsafe]
});

View file

@ -0,0 +1,9 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
export { core as CORE_SCHEMA } from "./core.ts";
export { def as DEFAULT_SCHEMA } from "./default.ts";
export { failsafe as FAILSAFE_SCHEMA } from "./failsafe.ts";
export { json as JSON_SCHEMA } from "./json.ts";

View file

@ -0,0 +1,11 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { SchemaDefinition } from "./schema.ts";
import { DEFAULT_SCHEMA } from "./schema/mod.ts";
export abstract class State {
constructor(public schema: SchemaDefinition = DEFAULT_SCHEMA) {}
}

View file

@ -0,0 +1,18 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { dump } from "./dumper/dumper.ts";
import { DumperStateOptions } from "./dumper/dumper_state.ts";
export type DumpOptions = DumperStateOptions;
/**
* Serializes `object` as a YAML document.
*
* You can disable exceptions by setting the skipInvalid option to true.
*/
export function stringify(obj: object, options?: DumpOptions): string {
return dump(obj, options);
}

View file

@ -0,0 +1,42 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { test } from "../../testing/mod.ts";
import { assertEquals } from "../../testing/asserts.ts";
import { stringify } from "./stringify.ts";
test({
name: "stringified correctly",
fn(): void {
const FIXTURE = {
foo: {
bar: true,
test: [
"a",
"b",
{
a: false
},
{
a: false
}
]
},
test: "foobar"
};
const ASSERTS = `foo:
bar: true
test:
- a
- b
- a: false
- a: false
test: foobar
`;
assertEquals(stringify(FIXTURE), ASSERTS);
}
});

55
std/encoding/yaml/type.ts Normal file
View file

@ -0,0 +1,55 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { ArrayObject, Any } from "./utils.ts";
export type KindType = "sequence" | "scalar" | "mapping";
export type StyleVariant = "lowercase" | "uppercase" | "camelcase" | "decimal";
export type RepresentFn = (data: Any, style?: StyleVariant) => Any;
const DEFAULT_RESOLVE = (): boolean => true;
const DEFAULT_CONSTRUCT = (data: Any): Any => data;
interface TypeOptions {
kind: KindType;
resolve?: (data: Any) => boolean;
construct?: (data: string) => Any;
instanceOf?: Any;
predicate?: (data: object) => boolean;
represent?: RepresentFn | ArrayObject<RepresentFn>;
defaultStyle?: StyleVariant;
styleAliases?: ArrayObject;
}
function checkTagFormat(tag: string): string {
return tag;
}
export class Type {
public tag: string;
public kind: KindType | null = null;
public instanceOf: Any;
public predicate?: (data: object) => boolean;
public represent?: RepresentFn | ArrayObject<RepresentFn>;
public defaultStyle?: StyleVariant;
public styleAliases?: ArrayObject;
public loadKind?: KindType;
constructor(tag: string, options?: TypeOptions) {
this.tag = checkTagFormat(tag);
if (options) {
this.kind = options.kind;
this.resolve = options.resolve || DEFAULT_RESOLVE;
this.construct = options.construct || DEFAULT_CONSTRUCT;
this.instanceOf = options.instanceOf;
this.predicate = options.predicate;
this.represent = options.represent;
this.defaultStyle = options.defaultStyle;
this.styleAliases = options.styleAliases;
}
}
public resolve: (data?: Any) => boolean = (): boolean => true;
public construct: (data?: Any) => Any = (data): Any => data;
}

View file

@ -0,0 +1,139 @@
// Ported from js-yaml v3.13.1:
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { Any } from "../utils.ts";
const { Buffer } = Deno;
// [ 64, 65, 66 ] -> [ padding, CR, LF ]
const BASE64_MAP =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r";
function resolveYamlBinary(data: Any): boolean {
if (data === null) return false;
let code: number;
let bitlen = 0;
const max = data.length;
const map = BASE64_MAP;
// Convert one by one.
for (let idx = 0; idx < max; idx++) {
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: string): Deno.Buffer {
// remove CR/LF & padding to simplify scan
const input = data.replace(/[\r\n=]/g, "");
const max = input.length;
const map = BASE64_MAP;
// Collect by 6*4 bits (3 bytes)
const result = [];
let bits = 0;
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 Buffer(new Uint8Array(result));
}
function representYamlBinary(object: Uint8Array): string {
const max = object.length;
const map = BASE64_MAP;
// Convert every three bytes to 4 ASCII characters.
let result = "";
let bits = 0;
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: Any): obj is Deno.Buffer {
const buf = new Buffer();
try {
if (0 > buf.readFromSync(obj as Deno.Buffer)) return true;
return false;
} catch {
return false;
} finally {
buf.reset();
}
}
export const binary = new Type("tag:yaml.org,2002:binary", {
construct: constructYamlBinary,
kind: "scalar",
predicate: isBinary,
represent: representYamlBinary,
resolve: resolveYamlBinary
});

View file

@ -0,0 +1,39 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { isBoolean } from "../utils.ts";
function resolveYamlBoolean(data: string): boolean {
const max = data.length;
return (
(max === 4 && (data === "true" || data === "True" || data === "TRUE")) ||
(max === 5 && (data === "false" || data === "False" || data === "FALSE"))
);
}
function constructYamlBoolean(data: string): boolean {
return data === "true" || data === "True" || data === "TRUE";
}
export const bool = new Type("tag:yaml.org,2002:bool", {
construct: constructYamlBoolean,
defaultStyle: "lowercase",
kind: "scalar",
predicate: isBoolean,
represent: {
lowercase(object: boolean): string {
return object ? "true" : "false";
},
uppercase(object: boolean): string {
return object ? "TRUE" : "FALSE";
},
camelcase(object: boolean): string {
return object ? "True" : "False";
}
},
resolve: resolveYamlBoolean
});

View file

@ -0,0 +1,125 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { StyleVariant, Type } from "../type.ts";
import { isNegativeZero, Any } from "../utils.ts";
const YAML_FLOAT_PATTERN = new RegExp(
// 2.5e4, 2.5 and integers
"^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?" +
// .2e4, .2
// special case, seems not from spec
"|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?" +
// 20:59
"|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*" +
// .inf
"|[-+]?\\.(?:inf|Inf|INF)" +
// .nan
"|\\.(?:nan|NaN|NAN))$"
);
function resolveYamlFloat(data: string): boolean {
if (
!YAML_FLOAT_PATTERN.test(data) ||
// Quick hack to not allow integers end with `_`
// Probably should update regexp & check speed
data[data.length - 1] === "_"
) {
return false;
}
return true;
}
function constructYamlFloat(data: string): number {
let value = data.replace(/_/g, "").toLowerCase();
const sign = value[0] === "-" ? -1 : 1;
const digits: number[] = [];
if ("+-".indexOf(value[0]) >= 0) {
value = value.slice(1);
}
if (value === ".inf") {
return sign === 1 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
}
if (value === ".nan") {
return NaN;
}
if (value.indexOf(":") >= 0) {
value.split(":").forEach((v): void => {
digits.unshift(parseFloat(v));
});
let valueNb = 0.0;
let base = 1;
digits.forEach((d): void => {
valueNb += d * base;
base *= 60;
});
return sign * valueNb;
}
return sign * parseFloat(value);
}
const SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/;
function representYamlFloat(object: Any, style?: StyleVariant): Any {
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 (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: Any): boolean {
return (
Object.prototype.toString.call(object) === "[object Number]" &&
(object % 1 !== 0 || isNegativeZero(object))
);
}
export const float = new Type("tag:yaml.org,2002:float", {
construct: constructYamlFloat,
defaultStyle: "lowercase",
kind: "scalar",
predicate: isFloat,
represent: representYamlFloat,
resolve: resolveYamlFloat
});

View file

@ -0,0 +1,191 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { isNegativeZero, Any } from "../utils.ts";
function isHexCode(c: number): boolean {
return (
(0x30 /* 0 */ <= c && c <= 0x39) /* 9 */ ||
(0x41 /* A */ <= c && c <= 0x46) /* F */ ||
(0x61 /* a */ <= c && c <= 0x66) /* f */
);
}
function isOctCode(c: number): boolean {
return 0x30 /* 0 */ <= c && c <= 0x37 /* 7 */;
}
function isDecCode(c: number): boolean {
return 0x30 /* 0 */ <= c && c <= 0x39 /* 9 */;
}
function resolveYamlInteger(data: string): boolean {
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 === "_") continue;
if (ch !== "0" && ch !== "1") return false;
hasDigits = true;
}
return hasDigits && ch !== "_";
}
if (ch === "x") {
// base 16
index++;
for (; index < max; index++) {
ch = data[index];
if (ch === "_") continue;
if (!isHexCode(data.charCodeAt(index))) return false;
hasDigits = true;
}
return hasDigits && ch !== "_";
}
// base 8
for (; index < max; index++) {
ch = data[index];
if (ch === "_") continue;
if (!isOctCode(data.charCodeAt(index))) return false;
hasDigits = true;
}
return hasDigits && ch !== "_";
}
// base 10 (except 0) or base 60
// value should not start with `_`;
if (ch === "_") return false;
for (; index < max; index++) {
ch = data[index];
if (ch === "_") continue;
if (ch === ":") break;
if (!isDecCode(data.charCodeAt(index))) {
return false;
}
hasDigits = true;
}
// Should have digits and should not end with `_`
if (!hasDigits || ch === "_") return false;
// if !base60 - done;
if (ch !== ":") return true;
// base60 almost not used, no needs to optimize
return /^(:[0-5]?[0-9])+$/.test(data.slice(index));
}
function constructYamlInteger(data: string): number {
let value = data;
const digits: number[] = [];
if (value.indexOf("_") !== -1) {
value = value.replace(/_/g, "");
}
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, 16);
return sign * parseInt(value, 8);
}
if (value.indexOf(":") !== -1) {
value.split(":").forEach((v): void => {
digits.unshift(parseInt(v, 10));
});
let valueInt = 0;
let base = 1;
digits.forEach((d): void => {
valueInt += d * base;
base *= 60;
});
return sign * valueInt;
}
return sign * parseInt(value, 10);
}
function isInteger(object: Any): boolean {
return (
Object.prototype.toString.call(object) === "[object Number]" &&
object % 1 === 0 &&
!isNegativeZero(object)
);
}
export const int = new Type("tag:yaml.org,2002:int", {
construct: constructYamlInteger,
defaultStyle: "decimal",
kind: "scalar",
predicate: isInteger,
represent: {
binary(obj: number): string {
return obj >= 0
? `0b${obj.toString(2)}`
: `-0b${obj.toString(2).slice(1)}`;
},
octal(obj: number): string {
return obj >= 0 ? `0${obj.toString(8)}` : `-0${obj.toString(8).slice(1)}`;
},
decimal(obj: number): string {
return obj.toString(10);
},
hexadecimal(obj: number): string {
return obj >= 0
? `0x${obj.toString(16).toUpperCase()}`
: `-0x${obj
.toString(16)
.toUpperCase()
.slice(1)}`;
}
},
resolve: resolveYamlInteger,
styleAliases: {
binary: [2, "bin"],
decimal: [10, "dec"],
hexadecimal: [16, "hex"],
octal: [8, "oct"]
}
});

View file

@ -0,0 +1,14 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { Any } from "../utils.ts";
export const map = new Type("tag:yaml.org,2002:map", {
construct(data): Any {
return data !== null ? data : {};
},
kind: "mapping"
});

View file

@ -0,0 +1,15 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
function resolveYamlMerge(data: string): boolean {
return data === "<<" || data === null;
}
export const merge = new Type("tag:yaml.org,2002:merge", {
kind: "scalar",
resolve: resolveYamlMerge
});

View file

@ -0,0 +1,18 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
export { binary } from "./binary.ts";
export { bool } from "./bool.ts";
export { float } from "./float.ts";
export { int } from "./int.ts";
export { map } from "./map.ts";
export { merge } from "./merge.ts";
export { nil } from "./nil.ts";
export { omap } from "./omap.ts";
export { pairs } from "./pairs.ts";
export { seq } from "./seq.ts";
export { set } from "./set.ts";
export { str } from "./str.ts";
export { timestamp } from "./timestamp.ts";

View file

@ -0,0 +1,45 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
function resolveYamlNull(data: string): boolean {
const max = data.length;
return (
(max === 1 && data === "~") ||
(max === 4 && (data === "null" || data === "Null" || data === "NULL"))
);
}
function constructYamlNull(): null {
return null;
}
function isNull(object: unknown): object is null {
return object === null;
}
export const nil = new Type("tag:yaml.org,2002:null", {
construct: constructYamlNull,
defaultStyle: "lowercase",
kind: "scalar",
predicate: isNull,
represent: {
canonical(): string {
return "~";
},
lowercase(): string {
return "null";
},
uppercase(): string {
return "NULL";
},
camelcase(): string {
return "Null";
}
},
resolve: resolveYamlNull
});

View file

@ -0,0 +1,46 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { Any } from "../utils.ts";
const _hasOwnProperty = Object.prototype.hasOwnProperty;
const _toString = Object.prototype.toString;
function resolveYamlOmap(data: Any): boolean {
const objectKeys: string[] = [];
let pairKey = "";
let pairHasKey = false;
for (const pair of data) {
pairHasKey = false;
if (_toString.call(pair) !== "[object Object]") return false;
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: Any): Any {
return data !== null ? data : [];
}
export const omap = new Type("tag:yaml.org,2002:omap", {
construct: constructYamlOmap,
kind: "sequence",
resolve: resolveYamlOmap
});

View file

@ -0,0 +1,49 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { Any } from "../utils.ts";
const _toString = Object.prototype.toString;
function resolveYamlPairs(data: Any[][]): boolean {
const result = new Array(data.length);
for (let index = 0; index < data.length; index++) {
const pair = data[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] as Any]];
}
return true;
}
function constructYamlPairs(data: string): Any[] {
if (data === null) return [];
const result = new Array(data.length);
for (let index = 0; index < data.length; index += 1) {
const pair = data[index];
const keys = Object.keys(pair);
result[index] = [keys[0], pair[keys[0] as Any]];
}
return result;
}
export const pairs = new Type("tag:yaml.org,2002:pairs", {
construct: constructYamlPairs,
kind: "sequence",
resolve: resolveYamlPairs
});

View file

@ -0,0 +1,14 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { Any } from "../utils.ts";
export const seq = new Type("tag:yaml.org,2002:seq", {
construct(data): Any {
return data !== null ? data : [];
},
kind: "sequence"
});

View file

@ -0,0 +1,31 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
import { Any } from "../utils.ts";
const _hasOwnProperty = Object.prototype.hasOwnProperty;
function resolveYamlSet(data: Any): boolean {
if (data === null) return true;
for (const key in data) {
if (_hasOwnProperty.call(data, key)) {
if (data[key] !== null) return false;
}
}
return true;
}
function constructYamlSet(data: string): Any {
return data !== null ? data : {};
}
export const set = new Type("tag:yaml.org,2002:set", {
construct: constructYamlSet,
kind: "mapping",
resolve: resolveYamlSet
});

View file

@ -0,0 +1,12 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
export const str = new Type("tag:yaml.org,2002:str", {
construct(data): string {
return data !== null ? data : "";
},
kind: "scalar"
});

View file

@ -0,0 +1,96 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import { Type } from "../type.ts";
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] tz_hour
"(?::([0-9][0-9]))?))?$" // [11] tz_minute
);
function resolveYamlTimestamp(data: string): boolean {
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: string): Date {
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];
let fraction = 0;
if (match[7]) {
let partFraction = match[7].slice(0, 3);
while (partFraction.length < 3) {
// milli-seconds
partFraction += "0";
}
fraction = +partFraction;
}
// match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute
let delta = null;
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(date: Date): string {
return date.toISOString();
}
export const timestamp = new Type("tag:yaml.org,2002:timestamp", {
construct: constructYamlTimestamp,
instanceOf: Date,
kind: "scalar",
represent: representYamlTimestamp,
resolve: resolveYamlTimestamp
});

View file

@ -0,0 +1,84 @@
// Ported from js-yaml v3.13.1:
// https://github.com/nodeca/js-yaml/commit/665aadda42349dcae869f12040d9b10ef18d12da
// Copyright 2011-2015 by Vitaly Puzrin. All rights reserved. MIT license.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
/* eslint-disable-next-line @typescript-eslint/no-explicit-any */
export type Any = any;
export function isNothing(subject: unknown): subject is never {
return typeof subject === "undefined" || subject === null;
}
export function isArray(value: unknown): value is Any[] {
return Array.isArray(value);
}
export function isBoolean(value: unknown): value is boolean {
return typeof value === "boolean" || value instanceof Boolean;
}
export function isNull(value: unknown): value is null {
return value === null;
}
export function isNullOrUndefined(value: unknown): value is null | undefined {
return value === null || value === undefined;
}
export function isNumber(value: unknown): value is number {
return typeof value === "number" || value instanceof Number;
}
export function isString(value: unknown): value is string {
return typeof value === "string" || value instanceof String;
}
export function isSymbol(value: unknown): value is symbol {
return typeof value === "symbol";
}
export function isUndefined(value: unknown): value is undefined {
return value === undefined;
}
export function isObject(value: unknown): value is object {
return value !== null && typeof value === "object";
}
export function isError(e: unknown): boolean {
return e instanceof Error;
}
export function isFunction(value: unknown): value is () => void {
return typeof value === "function";
}
export function isRegExp(value: unknown): value is RegExp {
return value instanceof RegExp;
}
export function toArray<T>(sequence: T): T | [] | [T] {
if (isArray(sequence)) return sequence;
if (isNothing(sequence)) return [];
return [sequence];
}
export function repeat(str: string, count: number): string {
let result = "";
for (let cycle = 0; cycle < count; cycle++) {
result += str;
}
return result;
}
export function isNegativeZero(i: number): boolean {
return i === 0 && Number.NEGATIVE_INFINITY === 1 / i;
}
export interface ArrayObject<T = Any> {
[P: string]: T;
}

View file

@ -0,0 +1,4 @@
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
import "./yaml/parse_test.ts";
import "./yaml/stringify_test.ts";