mirror of
https://github.com/denoland/deno.git
synced 2025-01-09 07:39:15 -05:00
feat: Add std/encoding/yaml module (#3361)
This commit is contained in:
parent
a2f5bccad7
commit
5671d38d8f
41 changed files with 4597 additions and 0 deletions
|
@ -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
8
std/encoding/yaml.ts
Normal 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";
|
899
std/encoding/yaml/dumper/dumper.ts
Normal file
899
std/encoding/yaml/dumper/dumper.ts
Normal 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 isn’t 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 "";
|
||||
}
|
138
std/encoding/yaml/dumper/dumper_state.ts
Normal file
138
std/encoding/yaml/dumper/dumper_state.ts
Normal 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;
|
||||
}
|
||||
}
|
22
std/encoding/yaml/error.ts
Normal file
22
std/encoding/yaml/error.ts
Normal 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}`;
|
||||
}
|
||||
}
|
22
std/encoding/yaml/example/dump.ts
Normal file
22
std/encoding/yaml/example/dump.ts
Normal 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"
|
||||
})
|
||||
);
|
27
std/encoding/yaml/example/inout.ts
Normal file
27
std/encoding/yaml/example/inout.ts
Normal 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.");
|
||||
}
|
19
std/encoding/yaml/example/parse.ts
Normal file
19
std/encoding/yaml/example/parse.ts
Normal 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);
|
||||
}
|
23
std/encoding/yaml/example/sample_document.ts
Normal file
23
std/encoding/yaml/example/sample_document.ts
Normal 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++;
|
||||
}
|
||||
})();
|
197
std/encoding/yaml/example/sample_document.yml
Normal file
197
std/encoding/yaml/example/sample_document.yml
Normal 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!';
|
||||
# }
|
1798
std/encoding/yaml/loader/loader.ts
Normal file
1798
std/encoding/yaml/loader/loader.ts
Normal file
File diff suppressed because it is too large
Load diff
74
std/encoding/yaml/loader/loader_state.ts
Normal file
74
std/encoding/yaml/loader/loader_state.ts
Normal 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
77
std/encoding/yaml/mark.ts
Normal 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;
|
||||
}
|
||||
}
|
32
std/encoding/yaml/parse.ts
Normal file
32
std/encoding/yaml/parse.ts
Normal 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);
|
||||
}
|
25
std/encoding/yaml/parse_test.ts
Normal file
25
std/encoding/yaml/parse_test.ts
Normal 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
101
std/encoding/yaml/schema.ts
Normal 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[];
|
||||
}
|
13
std/encoding/yaml/schema/core.ts
Normal file
13
std/encoding/yaml/schema/core.ts
Normal 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]
|
||||
});
|
16
std/encoding/yaml/schema/default.ts
Normal file
16
std/encoding/yaml/schema/default.ts
Normal 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]
|
||||
});
|
13
std/encoding/yaml/schema/failsafe.ts
Normal file
13
std/encoding/yaml/schema/failsafe.ts
Normal 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]
|
||||
});
|
15
std/encoding/yaml/schema/json.ts
Normal file
15
std/encoding/yaml/schema/json.ts
Normal 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]
|
||||
});
|
9
std/encoding/yaml/schema/mod.ts
Normal file
9
std/encoding/yaml/schema/mod.ts
Normal 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";
|
11
std/encoding/yaml/state.ts
Normal file
11
std/encoding/yaml/state.ts
Normal 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) {}
|
||||
}
|
18
std/encoding/yaml/stringify.ts
Normal file
18
std/encoding/yaml/stringify.ts
Normal 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);
|
||||
}
|
42
std/encoding/yaml/stringify_test.ts
Normal file
42
std/encoding/yaml/stringify_test.ts
Normal 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
55
std/encoding/yaml/type.ts
Normal 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;
|
||||
}
|
139
std/encoding/yaml/type/binary.ts
Normal file
139
std/encoding/yaml/type/binary.ts
Normal 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
|
||||
});
|
39
std/encoding/yaml/type/bool.ts
Normal file
39
std/encoding/yaml/type/bool.ts
Normal 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
|
||||
});
|
125
std/encoding/yaml/type/float.ts
Normal file
125
std/encoding/yaml/type/float.ts
Normal 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
|
||||
});
|
191
std/encoding/yaml/type/int.ts
Normal file
191
std/encoding/yaml/type/int.ts
Normal 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"]
|
||||
}
|
||||
});
|
14
std/encoding/yaml/type/map.ts
Normal file
14
std/encoding/yaml/type/map.ts
Normal 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"
|
||||
});
|
15
std/encoding/yaml/type/merge.ts
Normal file
15
std/encoding/yaml/type/merge.ts
Normal 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
|
||||
});
|
18
std/encoding/yaml/type/mod.ts
Normal file
18
std/encoding/yaml/type/mod.ts
Normal 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";
|
45
std/encoding/yaml/type/nil.ts
Normal file
45
std/encoding/yaml/type/nil.ts
Normal 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
|
||||
});
|
46
std/encoding/yaml/type/omap.ts
Normal file
46
std/encoding/yaml/type/omap.ts
Normal 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
|
||||
});
|
49
std/encoding/yaml/type/pairs.ts
Normal file
49
std/encoding/yaml/type/pairs.ts
Normal 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
|
||||
});
|
14
std/encoding/yaml/type/seq.ts
Normal file
14
std/encoding/yaml/type/seq.ts
Normal 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"
|
||||
});
|
31
std/encoding/yaml/type/set.ts
Normal file
31
std/encoding/yaml/type/set.ts
Normal 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
|
||||
});
|
12
std/encoding/yaml/type/str.ts
Normal file
12
std/encoding/yaml/type/str.ts
Normal 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"
|
||||
});
|
96
std/encoding/yaml/type/timestamp.ts
Normal file
96
std/encoding/yaml/type/timestamp.ts
Normal 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
|
||||
});
|
84
std/encoding/yaml/utils.ts
Normal file
84
std/encoding/yaml/utils.ts
Normal 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;
|
||||
}
|
4
std/encoding/yaml_test.ts
Normal file
4
std/encoding/yaml_test.ts
Normal 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";
|
Loading…
Reference in a new issue