diff --git a/.gitignore b/.gitignore index af7c9e4793ff..41c93c7bd84d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ deno.d.ts node_modules package.json package-lock.json +yarn.lock .vscode diff --git a/encoding/README.md b/encoding/README.md index f03e80ba2515..550b10b36b42 100644 --- a/encoding/README.md +++ b/encoding/README.md @@ -217,3 +217,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 diff --git a/encoding/test.ts b/encoding/test.ts index c7a1c9716007..8cb2c5d4e3ad 100644 --- a/encoding/test.ts +++ b/encoding/test.ts @@ -2,3 +2,4 @@ import "./hex_test.ts"; import "./toml_test.ts"; import "./csv_test.ts"; +import "./yaml_test.ts"; diff --git a/encoding/yaml.ts b/encoding/yaml.ts new file mode 100644 index 000000000000..055edad72b23 --- /dev/null +++ b/encoding/yaml.ts @@ -0,0 +1,8 @@ +// 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. +// 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"; diff --git a/encoding/yaml/Mark.ts b/encoding/yaml/Mark.ts new file mode 100644 index 000000000000..96c7ba8524a6 --- /dev/null +++ b/encoding/yaml/Mark.ts @@ -0,0 +1,76 @@ +// 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 { 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; + } +} diff --git a/encoding/yaml/Schema.ts b/encoding/yaml/Schema.ts new file mode 100644 index 000000000000..f43e5997fb02 --- /dev/null +++ b/encoding/yaml/Schema.ts @@ -0,0 +1,101 @@ +// 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. +// 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 }; +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[]; +} diff --git a/encoding/yaml/State.ts b/encoding/yaml/State.ts new file mode 100644 index 000000000000..3e20e5428b30 --- /dev/null +++ b/encoding/yaml/State.ts @@ -0,0 +1,11 @@ +// 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. +// 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) {} +} diff --git a/encoding/yaml/Type.ts b/encoding/yaml/Type.ts new file mode 100644 index 000000000000..59030616eaf4 --- /dev/null +++ b/encoding/yaml/Type.ts @@ -0,0 +1,55 @@ +// 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. +// 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; + 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; + 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; +} diff --git a/encoding/yaml/__test__/00-units.js b/encoding/yaml/__test__/00-units.js new file mode 100644 index 000000000000..fe8cfe1a3554 --- /dev/null +++ b/encoding/yaml/__test__/00-units.js @@ -0,0 +1,17 @@ +// 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. + +var path = require('path'); +var fs = require('fs'); + + +suite('Units', function () { + var directory = path.resolve(__dirname, 'units'); + + fs.readdirSync(directory).forEach(function (file) { + if (path.extname(file) === '.js') { + require(path.resolve(directory, file)); + } + }); +}); diff --git a/encoding/yaml/__test__/10-loader.js b/encoding/yaml/__test__/10-loader.js new file mode 100644 index 000000000000..a3088b052baf --- /dev/null +++ b/encoding/yaml/__test__/10-loader.js @@ -0,0 +1,39 @@ +// 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. + +var assert = require('assert'); +var path = require('path'); +var fs = require('fs'); +var yaml = require('../'); + +var TEST_SCHEMA = require('./support/schema').TEST_SCHEMA; + + +suite('Loader', function () { + var samplesDir = path.resolve(__dirname, 'samples-common'); + + fs.readdirSync(samplesDir).forEach(function (jsFile) { + if (path.extname(jsFile) !== '.js') return; // continue + + var yamlFile = path.resolve(samplesDir, path.basename(jsFile, '.js') + '.yml'); + + test(path.basename(jsFile, '.js'), function () { + var expected = require(path.resolve(samplesDir, jsFile)); + var actual = []; + + yaml.loadAll(fs.readFileSync(yamlFile, { encoding: 'utf8' }), function (doc) { actual.push(doc); }, { + filename: yamlFile, + schema: TEST_SCHEMA + }); + + if (actual.length === 1) actual = actual[0]; + + if (typeof expected === 'function') { + expected.call(this, actual); + } else { + assert.deepEqual(actual, expected); + } + }); + }); +}); diff --git a/encoding/yaml/__test__/11-load-errors.js b/encoding/yaml/__test__/11-load-errors.js new file mode 100644 index 000000000000..01f12494b4a1 --- /dev/null +++ b/encoding/yaml/__test__/11-load-errors.js @@ -0,0 +1,35 @@ +// 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. + +var assert = require('assert'); +var path = require('path'); +var fs = require('fs'); +var yaml = require('../'); + +var TEST_SCHEMA = require('./support/schema').TEST_SCHEMA; + + +suite('Load errors', function () { + var samplesDir = path.resolve(__dirname, 'samples-load-errors'); + + fs.readdirSync(samplesDir).forEach(function (sampleName) { + var yamlFile = path.resolve(samplesDir, sampleName); + + test(path.basename(sampleName, '.yml'), function () { + var yamlSource = fs.readFileSync(yamlFile, { encoding: 'utf8' }); + + assert.throws(function () { + yaml.loadAll( + yamlSource, + function () {}, + { + filename: yamlFile, + schema: TEST_SCHEMA, + onWarning: function (e) { throw e; } + } + ); + }, yaml.YAMLException, yamlFile); + }); + }); +}); diff --git a/encoding/yaml/__test__/20-dumper.js b/encoding/yaml/__test__/20-dumper.js new file mode 100644 index 000000000000..714dd232bdc9 --- /dev/null +++ b/encoding/yaml/__test__/20-dumper.js @@ -0,0 +1,32 @@ +// 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. + +var assert = require('assert'); +var path = require('path'); +var fs = require('fs'); +var yaml = require('../'); + +var TEST_SCHEMA = require('./support/schema').TEST_SCHEMA; + + +suite('Dumper', function () { + var samplesDir = path.resolve(__dirname, 'samples-common'); + + fs.readdirSync(samplesDir).forEach(function (jsFile) { + if (path.extname(jsFile) !== '.js') return; // continue + + test(path.basename(jsFile, '.js'), function () { + var sample = require(path.resolve(samplesDir, jsFile)); + var data = typeof sample === 'function' ? sample.expected : sample, + serialized = yaml.dump(data, { schema: TEST_SCHEMA }), + deserialized = yaml.load(serialized, { schema: TEST_SCHEMA }); + + if (typeof sample === 'function') { + sample.call(this, deserialized); + } else { + assert.deepEqual(deserialized, sample); + } + }); + }); +}); diff --git a/encoding/yaml/__test__/25-dumper-fuzzy.js b/encoding/yaml/__test__/25-dumper-fuzzy.js new file mode 100644 index 000000000000..2f7adbc8649e --- /dev/null +++ b/encoding/yaml/__test__/25-dumper-fuzzy.js @@ -0,0 +1,49 @@ +// 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. + +var assert = require('assert'); +var fc = require('fast-check'); +var yaml = require('../'); + +// Generate valid YAML instances for yaml.safeDump +var key = fc.string16bits(); +var values = [ + key, fc.boolean(), fc.integer(), fc.double(), + fc.constantFrom(null, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY) +]; +var yamlArbitrary = fc.object({ key: key, values: values }); + +// Generate valid options for yaml.safeDump configuration +var dumpOptionsArbitrary = fc.record({ + skipInvalid: fc.boolean(), + sortKeys: fc.boolean(), + noRefs: fc.boolean(), + noCompatMode: fc.boolean(), + condenseFlow: fc.boolean(), + indent: fc.integer(1, 80), + flowLevel: fc.integer(-1, 10), + styles: fc.record({ + '!!null': fc.constantFrom('lowercase', 'canonical', 'uppercase', 'camelcase'), + '!!int': fc.constantFrom('decimal', 'binary', 'octal', 'hexadecimal'), + '!!bool': fc.constantFrom('lowercase', 'uppercase', 'camelcase'), + '!!float': fc.constantFrom('lowercase', 'uppercase', 'camelcase') + }, { with_deleted_keys: true }) +}, { with_deleted_keys: true }) + .map(function (instance) { + if (instance.condenseFlow === true && instance.flowLevel !== undefined) { instance.flowLevel = -1; } + return instance; + }); + +suite('Properties', function () { + test('Load from dumped should be the original object', function () { + fc.assert(fc.property( + yamlArbitrary, + dumpOptionsArbitrary, + function (obj, dumpOptions) { + var yamlContent = yaml.safeDump(obj, dumpOptions); + assert.ok(typeof yamlContent === 'string'); + assert.deepStrictEqual(yaml.safeLoad(yamlContent), obj); + })); + }); +}); diff --git a/encoding/yaml/__test__/30-issues.js b/encoding/yaml/__test__/30-issues.js new file mode 100644 index 000000000000..a409cd43f47d --- /dev/null +++ b/encoding/yaml/__test__/30-issues.js @@ -0,0 +1,17 @@ +// 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. + +var path = require('path'); +var fs = require('fs'); + + +suite('Issues', function () { + var issues = path.resolve(__dirname, 'issues'); + + fs.readdirSync(issues).forEach(function (file) { + if (path.extname(file) === '.js') { + require(path.resolve(issues, file)); + } + }); +}); diff --git a/encoding/yaml/__test__/issues/0008.js b/encoding/yaml/__test__/issues/0008.js new file mode 100644 index 000000000000..6c6b1effd8ec --- /dev/null +++ b/encoding/yaml/__test__/issues/0008.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Parse failed when no document start present', function () { + assert.doesNotThrow(function () { + yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0008.yml'), 'utf8')); + }, TypeError); +}); diff --git a/encoding/yaml/__test__/issues/0008.yml b/encoding/yaml/__test__/issues/0008.yml new file mode 100644 index 000000000000..ac9920457b18 --- /dev/null +++ b/encoding/yaml/__test__/issues/0008.yml @@ -0,0 +1 @@ +foo: !!str bar diff --git a/encoding/yaml/__test__/issues/0017.js b/encoding/yaml/__test__/issues/0017.js new file mode 100644 index 000000000000..92b3c88ebe8e --- /dev/null +++ b/encoding/yaml/__test__/issues/0017.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Non-specific "!" tags should resolve to !!str', function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0017.yml'), 'utf8')); + + assert.strictEqual(typeof data, 'string'); +}); diff --git a/encoding/yaml/__test__/issues/0017.yml b/encoding/yaml/__test__/issues/0017.yml new file mode 100644 index 000000000000..27ca721018fa --- /dev/null +++ b/encoding/yaml/__test__/issues/0017.yml @@ -0,0 +1 @@ +! 12 diff --git a/encoding/yaml/__test__/issues/0019.js b/encoding/yaml/__test__/issues/0019.js new file mode 100644 index 000000000000..90e7f816f140 --- /dev/null +++ b/encoding/yaml/__test__/issues/0019.js @@ -0,0 +1,15 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Timestamp parsing is one month off', function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0019.yml'), 'utf8')); + + // JS month starts with 0 (0 => Jan, 1 => Feb, ...) + assert.strictEqual(data.xmas.getTime(), Date.UTC(2011, 11, 24)); +}); diff --git a/encoding/yaml/__test__/issues/0019.yml b/encoding/yaml/__test__/issues/0019.yml new file mode 100644 index 000000000000..79799fe98ccc --- /dev/null +++ b/encoding/yaml/__test__/issues/0019.yml @@ -0,0 +1,3 @@ +--- +xmas: 2011-12-24 +... diff --git a/encoding/yaml/__test__/issues/0026.js b/encoding/yaml/__test__/issues/0026.js new file mode 100644 index 000000000000..3172ec2182a5 --- /dev/null +++ b/encoding/yaml/__test__/issues/0026.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('should convert new line into white space', function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0026.yml'), 'utf8')); + + assert.strictEqual(data.test, 'a b c\n'); +}); diff --git a/encoding/yaml/__test__/issues/0026.yml b/encoding/yaml/__test__/issues/0026.yml new file mode 100644 index 000000000000..65d777e8ffcf --- /dev/null +++ b/encoding/yaml/__test__/issues/0026.yml @@ -0,0 +1,4 @@ +test: > + a + b + c diff --git a/encoding/yaml/__test__/issues/0033.js b/encoding/yaml/__test__/issues/0033.js new file mode 100644 index 000000000000..d3bd6768892e --- /dev/null +++ b/encoding/yaml/__test__/issues/0033.js @@ -0,0 +1,16 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('refactor compact variant of MarkedYAMLError.toString', function () { + var source = readFileSync(require('path').join(__dirname, '/0033.yml'), 'utf8'); + + assert.throws(function () { + yaml.safeLoad(source); + }, "require('issue-33.yml') should throw, but it does not"); +}); diff --git a/encoding/yaml/__test__/issues/0033.yml b/encoding/yaml/__test__/issues/0033.yml new file mode 100644 index 000000000000..3eca41c9f1a8 --- /dev/null +++ b/encoding/yaml/__test__/issues/0033.yml @@ -0,0 +1 @@ +foo: {bar} baz diff --git a/encoding/yaml/__test__/issues/0046.js b/encoding/yaml/__test__/issues/0046.js new file mode 100644 index 000000000000..f1e17365e58a --- /dev/null +++ b/encoding/yaml/__test__/issues/0046.js @@ -0,0 +1,29 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Timestamps are incorrectly parsed in local time', function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0046.yml'), 'utf8')), + date1, date2; + + date1 = data.date1; // date1: 2010-10-20T20:45:00Z + assert.strictEqual(date1.getUTCFullYear(), 2010, 'year'); + assert.strictEqual(date1.getUTCMonth(), 9, 'month'); + assert.strictEqual(date1.getUTCDate(), 20, 'date'); + assert.strictEqual(date1.getUTCHours(), 20); + assert.strictEqual(date1.getUTCMinutes(), 45); + assert.strictEqual(date1.getUTCSeconds(), 0); + + date2 = data.date2; // date2: 2010-10-20T20:45:00+0100 + assert.strictEqual(date2.getUTCFullYear(), 2010, 'year'); + assert.strictEqual(date2.getUTCMonth(), 9, 'month'); + assert.strictEqual(date2.getUTCDate(), 20, 'date'); + assert.strictEqual(date2.getUTCHours(), 19); + assert.strictEqual(date2.getUTCMinutes(), 45); + assert.strictEqual(date2.getUTCSeconds(), 0); +}); diff --git a/encoding/yaml/__test__/issues/0046.yml b/encoding/yaml/__test__/issues/0046.yml new file mode 100644 index 000000000000..fbe36bbee124 --- /dev/null +++ b/encoding/yaml/__test__/issues/0046.yml @@ -0,0 +1,2 @@ +date1: 2010-10-20T20:45:00Z +date2: 2010-10-20T20:45:00+01:00 diff --git a/encoding/yaml/__test__/issues/0054.js b/encoding/yaml/__test__/issues/0054.js new file mode 100644 index 000000000000..46d000d32086 --- /dev/null +++ b/encoding/yaml/__test__/issues/0054.js @@ -0,0 +1,29 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test("Incorrect utf-8 handling on require('file.yaml')", function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0054.yml'), 'utf8')), + expected = '', + index; + + // + // document is an array of 40 elements + // each element is a string of 100 `у` (Russian letter) chars + // + for (index = 0; index <= 100; index += 1) { + expected += 'у'; + } + + // + // make sure none of the strings were corrupted. + // + for (index = 0; index < 40; index += 1) { + assert.strictEqual(data[index], expected, ('Line ' + index + ' is corrupted')); + } +}); diff --git a/encoding/yaml/__test__/issues/0054.yml b/encoding/yaml/__test__/issues/0054.yml new file mode 100644 index 000000000000..a0c7a021bf86 --- /dev/null +++ b/encoding/yaml/__test__/issues/0054.yml @@ -0,0 +1,41 @@ +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу +- ууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууууу diff --git a/encoding/yaml/__test__/issues/0063.js b/encoding/yaml/__test__/issues/0063.js new file mode 100644 index 000000000000..6eb4e641ea9f --- /dev/null +++ b/encoding/yaml/__test__/issues/0063.js @@ -0,0 +1,24 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Invalid errors/warnings of invalid indentation on flow scalars', function () { + var sources = [ + 'text:\n hello\n world', // plain style + "text:\n 'hello\n world'", // single-quoted style + 'text:\n "hello\n world"' // double-quoted style + ]; + var expected = { text: 'hello world' }; + + assert.doesNotThrow(function () { yaml.load(sources[0]); }, 'Throws on plain style'); + assert.doesNotThrow(function () { yaml.load(sources[1]); }, 'Throws on single-quoted style'); + assert.doesNotThrow(function () { yaml.load(sources[2]); }, 'Throws on double-quoted style'); + + assert.deepEqual(yaml.load(sources[0]), expected); + assert.deepEqual(yaml.load(sources[1]), expected); + assert.deepEqual(yaml.load(sources[2]), expected); +}); diff --git a/encoding/yaml/__test__/issues/0064.js b/encoding/yaml/__test__/issues/0064.js new file mode 100644 index 000000000000..f7b8590739bb --- /dev/null +++ b/encoding/yaml/__test__/issues/0064.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Wrong error message when yaml file contains tabs', function () { + assert.doesNotThrow( + function () { yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0064.yml'), 'utf8')); }, + yaml.YAMLException); +}); diff --git a/encoding/yaml/__test__/issues/0064.yml b/encoding/yaml/__test__/issues/0064.yml new file mode 100644 index 000000000000..314140014f21 --- /dev/null +++ b/encoding/yaml/__test__/issues/0064.yml @@ -0,0 +1 @@ +mapping: contains tab diff --git a/encoding/yaml/__test__/issues/0068.js b/encoding/yaml/__test__/issues/0068.js new file mode 100644 index 000000000000..2e45c7d9aa17 --- /dev/null +++ b/encoding/yaml/__test__/issues/0068.js @@ -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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Prevent adding unnecessary space character to end of a line within block collections', function () { + assert.strictEqual(yaml.dump({ data: [ 'foo', 'bar', 'baz' ] }), 'data:\n - foo\n - bar\n - baz\n'); + assert.strictEqual(yaml.dump({ foo: { bar: [ 'baz' ] } }), 'foo:\n bar:\n - baz\n'); +}); diff --git a/encoding/yaml/__test__/issues/0085.js b/encoding/yaml/__test__/issues/0085.js new file mode 100644 index 000000000000..e9635e665215 --- /dev/null +++ b/encoding/yaml/__test__/issues/0085.js @@ -0,0 +1,25 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +var DEPRECATED_BOOLEANS_SYNTAX = [ + 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON', + 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF' +]; + + +test('Dumper should take into account booleans syntax from YAML 1.0/1.1', function () { + DEPRECATED_BOOLEANS_SYNTAX.forEach(function (string) { + var dump = yaml.dump(string).trim(); + + assert( + ((dump === "'" + string + "'") || (dump === '"' + string + '"')), + ('"' + string + '" string is dumped without quoting; actual dump: ' + dump) + ); + }); +}); + diff --git a/encoding/yaml/__test__/issues/0092.js b/encoding/yaml/__test__/issues/0092.js new file mode 100644 index 000000000000..cecfabddedc5 --- /dev/null +++ b/encoding/yaml/__test__/issues/0092.js @@ -0,0 +1,13 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Invalid parse error on whitespace between quoted scalar keys and ":" symbol in mappings', function () { + assert.doesNotThrow(function () { + yaml.load('{ "field1" : "v1", "field2": "v2" }'); + }); +}); diff --git a/encoding/yaml/__test__/issues/0093.js b/encoding/yaml/__test__/issues/0093.js new file mode 100644 index 000000000000..71b321caad06 --- /dev/null +++ b/encoding/yaml/__test__/issues/0093.js @@ -0,0 +1,16 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Unwanted line breaks in folded scalars', function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0093.yml'), 'utf8')); + + assert.strictEqual(data.first, 'a b\n c\n d\ne f\n'); + assert.strictEqual(data.second, 'a b\n c\n\n d\ne f\n'); + assert.strictEqual(data.third, 'a b\n\n c\n d\ne f\n'); +}); diff --git a/encoding/yaml/__test__/issues/0093.yml b/encoding/yaml/__test__/issues/0093.yml new file mode 100644 index 000000000000..fc99568d6350 --- /dev/null +++ b/encoding/yaml/__test__/issues/0093.yml @@ -0,0 +1,25 @@ +first: > + a + b + c + d + e + f + +second: > + a + b + c + + d + e + f + +third: > + a + b + + c + d + e + f diff --git a/encoding/yaml/__test__/issues/0095.js b/encoding/yaml/__test__/issues/0095.js new file mode 100644 index 000000000000..c75dc1047e24 --- /dev/null +++ b/encoding/yaml/__test__/issues/0095.js @@ -0,0 +1,25 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Empty block scalars loaded wrong', function () { + assert.deepEqual(yaml.load('a: |\nb: .'), { a: '', b: '.' }); + assert.deepEqual(yaml.load('a: |+\nb: .'), { a: '', b: '.' }); + assert.deepEqual(yaml.load('a: |-\nb: .'), { a: '', b: '.' }); + + assert.deepEqual(yaml.load('a: >\nb: .'), { a: '', b: '.' }); + assert.deepEqual(yaml.load('a: >+\nb: .'), { a: '', b: '.' }); + assert.deepEqual(yaml.load('a: >-\nb: .'), { a: '', b: '.' }); + + assert.deepEqual(yaml.load('a: |\n\nb: .'), { a: '', b: '.' }); + assert.deepEqual(yaml.load('a: |+\n\nb: .'), { a: '\n', b: '.' }); + assert.deepEqual(yaml.load('a: |-\n\nb: .'), { a: '', b: '.' }); + + assert.deepEqual(yaml.load('a: >\n\nb: .'), { a: '', b: '.' }); + assert.deepEqual(yaml.load('a: >+\n\nb: .'), { a: '\n', b: '.' }); + assert.deepEqual(yaml.load('a: >-\n\nb: .'), { a: '', b: '.' }); +}); diff --git a/encoding/yaml/__test__/issues/0108.js b/encoding/yaml/__test__/issues/0108.js new file mode 100644 index 000000000000..6de42035fa02 --- /dev/null +++ b/encoding/yaml/__test__/issues/0108.js @@ -0,0 +1,13 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Literal scalars have an unwanted leading line break', function () { + assert.strictEqual(yaml.load('|\n foobar\n'), 'foobar\n'); + assert.strictEqual(yaml.load('|\n hello\n world\n'), 'hello\nworld\n'); + assert.strictEqual(yaml.load('|\n war never changes\n'), 'war never changes\n'); +}); diff --git a/encoding/yaml/__test__/issues/0110.js b/encoding/yaml/__test__/issues/0110.js new file mode 100644 index 000000000000..a997450d1e2a --- /dev/null +++ b/encoding/yaml/__test__/issues/0110.js @@ -0,0 +1,29 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Circular and cross references', function () { + var source = { + a: { a: 1 }, + b: [ 1, 2 ], + c: {}, + d: [] + }; + source.crossObject = source.a; + source.crossArray = source.b; + source.c.circularObject = source; + source.d.push(source.d); + source.d.push(source); + + var obtained = yaml.load(yaml.dump(source)); + + assert.strictEqual(obtained.crossObject, obtained.a); + assert.strictEqual(obtained.crossArray, obtained.b); + assert.strictEqual(obtained.c.circularObject, obtained); + assert.strictEqual(obtained.d[0], obtained.d); + assert.strictEqual(obtained.d[1], obtained); +}); diff --git a/encoding/yaml/__test__/issues/0112.js b/encoding/yaml/__test__/issues/0112.js new file mode 100644 index 000000000000..e2a83fa58370 --- /dev/null +++ b/encoding/yaml/__test__/issues/0112.js @@ -0,0 +1,15 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Plain scalar "constructor" parsed as `null`', function () { + assert.strictEqual(yaml.load('constructor'), 'constructor'); + assert.deepEqual(yaml.load('constructor: value'), { constructor: 'value' }); + assert.deepEqual(yaml.load('key: constructor'), { key: 'constructor' }); + assert.deepEqual(yaml.load('{ constructor: value }'), { constructor: 'value' }); + assert.deepEqual(yaml.load('{ key: constructor }'), { key: 'constructor' }); +}); diff --git a/encoding/yaml/__test__/issues/0117.js b/encoding/yaml/__test__/issues/0117.js new file mode 100644 index 000000000000..3051ffcb0f2e --- /dev/null +++ b/encoding/yaml/__test__/issues/0117.js @@ -0,0 +1,11 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Negative zero loses the sign after dump', function () { + assert.strictEqual(yaml.dump(-0), '-0.0\n'); +}); diff --git a/encoding/yaml/__test__/issues/0123.js b/encoding/yaml/__test__/issues/0123.js new file mode 100644 index 000000000000..c42825fb5188 --- /dev/null +++ b/encoding/yaml/__test__/issues/0123.js @@ -0,0 +1,25 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('RegExps should be properly closed', function () { + assert.throws(function () { yaml.load('!!js/regexp /fo'); }); + assert.throws(function () { yaml.load('!!js/regexp /fo/q'); }); + assert.throws(function () { yaml.load('!!js/regexp /fo/giii'); }); + + // https://github.com/nodeca/js-yaml/issues/172 + var regexp = yaml.load('!!js/regexp /fo/g/g'); + assert.ok(regexp instanceof RegExp); + var regexpStr = regexp.toString(); + // Accept the old (slightly incorrect) V8, as well as the new V8 result + // TODO: Remove when/if Node 0.12 and below is no longer supported. + if (regexpStr === '/fo/g/g') { + assert.strictEqual(regexpStr, '/fo/g/g'); + } else { + assert.strictEqual(regexpStr, '/fo\\/g/g'); + } +}); diff --git a/encoding/yaml/__test__/issues/0144.js b/encoding/yaml/__test__/issues/0144.js new file mode 100644 index 000000000000..85b5c4fb0d73 --- /dev/null +++ b/encoding/yaml/__test__/issues/0144.js @@ -0,0 +1,11 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Infinite loop when attempting to parse multi-line scalar document that is not indented', function () { + assert.strictEqual(yaml.load('--- |\nfoo\n'), 'foo\n'); +}); diff --git a/encoding/yaml/__test__/issues/0154.js b/encoding/yaml/__test__/issues/0154.js new file mode 100644 index 000000000000..9c941d7e868c --- /dev/null +++ b/encoding/yaml/__test__/issues/0154.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Indentation warning on empty lines within quoted scalars and flow collections', function () { + assert.doesNotThrow(function () { yaml.load("- 'hello\n\n world'"); }); + assert.doesNotThrow(function () { yaml.load('- "hello\n\n world"'); }); + assert.doesNotThrow(function () { yaml.load('- [hello,\n\n world]'); }); + assert.doesNotThrow(function () { yaml.load('- {hello: world,\n\n foo: bar}'); }); +}); diff --git a/encoding/yaml/__test__/issues/0155.js b/encoding/yaml/__test__/issues/0155.js new file mode 100644 index 000000000000..4df52ba0a80f --- /dev/null +++ b/encoding/yaml/__test__/issues/0155.js @@ -0,0 +1,11 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Named null', function () { + assert.deepEqual(yaml.load('---\ntest: !!null \nfoo: bar'), { test: null, foo: 'bar' }); +}); diff --git a/encoding/yaml/__test__/issues/0156.js b/encoding/yaml/__test__/issues/0156.js new file mode 100644 index 000000000000..6b44ea7a6b3a --- /dev/null +++ b/encoding/yaml/__test__/issues/0156.js @@ -0,0 +1,21 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../lib/js-yaml'); + + +function SuccessSignal() {} + +var TestClassYaml = new yaml.Type('!test', { + kind: 'scalar', + resolve: function () { throw new SuccessSignal(); } +}); + +var TEST_SCHEMA = yaml.Schema.create([ TestClassYaml ]); + + +test('Resolving of empty nodes are skipped in some cases', function () { + assert.throws(function () { yaml.load('- foo: !test\n- bar: baz', { schema: TEST_SCHEMA }); }, SuccessSignal); +}); diff --git a/encoding/yaml/__test__/issues/0160.js b/encoding/yaml/__test__/issues/0160.js new file mode 100644 index 000000000000..8511c79d7d62 --- /dev/null +++ b/encoding/yaml/__test__/issues/0160.js @@ -0,0 +1,11 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Correct encoding of UTF-16 surrogate pairs', function () { + assert.strictEqual(yaml.load('"\\U0001F431"'), '🐱'); +}); diff --git a/encoding/yaml/__test__/issues/0194.js b/encoding/yaml/__test__/issues/0194.js new file mode 100644 index 000000000000..7a2c998fcd2b --- /dev/null +++ b/encoding/yaml/__test__/issues/0194.js @@ -0,0 +1,22 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Don\'t throw on warning', function () { + var src = readFileSync(require('path').join(__dirname, '/0194.yml'), 'utf8'), + warnings = [], + data; + + data = yaml.safeLoad(src); + + assert.deepEqual(data, { foo: { bar: true } }); + + yaml.safeLoad(src, { onWarning: function (e) { warnings.push(e); } }); + + assert.strictEqual(warnings.length, 1); +}); diff --git a/encoding/yaml/__test__/issues/0194.yml b/encoding/yaml/__test__/issues/0194.yml new file mode 100644 index 000000000000..6115793ab6d6 --- /dev/null +++ b/encoding/yaml/__test__/issues/0194.yml @@ -0,0 +1,3 @@ +foo: { + bar: true +} diff --git a/encoding/yaml/__test__/issues/0203.js b/encoding/yaml/__test__/issues/0203.js new file mode 100644 index 000000000000..868fa9d7396c --- /dev/null +++ b/encoding/yaml/__test__/issues/0203.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Don\'t throw on warning', function () { + var src = readFileSync(require('path').join(__dirname, '/0203.yml'), 'utf8'); + + assert.deepEqual(yaml.safeLoad(src), { test: '\n\nHello\nworld' }); +}); diff --git a/encoding/yaml/__test__/issues/0203.yml b/encoding/yaml/__test__/issues/0203.yml new file mode 100644 index 000000000000..742d60824f92 --- /dev/null +++ b/encoding/yaml/__test__/issues/0203.yml @@ -0,0 +1,5 @@ +test: |- + + + Hello + world \ No newline at end of file diff --git a/encoding/yaml/__test__/issues/0205.js b/encoding/yaml/__test__/issues/0205.js new file mode 100644 index 000000000000..4bd5b3e40bff --- /dev/null +++ b/encoding/yaml/__test__/issues/0205.js @@ -0,0 +1,28 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Duplicated objects within array', function () { + var obj = { test: 'canary' }; + var arrayWithRefs = [ obj, obj ]; + + var obtained = yaml.load(yaml.dump(arrayWithRefs)); + + assert.strictEqual(obtained[0].test, 'canary'); + assert.strictEqual(obtained[0], obtained[1]); +}); + +test('Duplicated arrays within array', function () { + var array = [ 0, 1 ]; + var arrayWithRefs = [ array, array ]; + + var obtained = yaml.load(yaml.dump(arrayWithRefs)); + + assert.strictEqual(obtained[0][0], 0); + assert.strictEqual(obtained[0][1], 1); + assert.strictEqual(obtained[0], obtained[1]); +}); diff --git a/encoding/yaml/__test__/issues/0217.js b/encoding/yaml/__test__/issues/0217.js new file mode 100644 index 000000000000..bdca3551be13 --- /dev/null +++ b/encoding/yaml/__test__/issues/0217.js @@ -0,0 +1,117 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +// Simplistic check for folded style header at the end of the first line. +function isFolded(s) { + return s.search(/^[^\n]*>[\-+]?\n/) !== -1; +} + +// Runs one cycle of dump then load. Also checks that dumped result is folded. +function loadAfterDump(input) { + var output = yaml.dump(input); + if (!isFolded(output)) { + assert.fail(output, '(first line should end with >-, >, or >+)', + 'Test cannot continue: folded style was expected'); + } + return yaml.load(output); +} + + +test('Folding Javascript functions preserves content', function () { + // Tests loading a function, then tests dumping and loading. + function assertFunctionPreserved(functionString, inputs, expectedOutputs, name) { + var f = yaml.load('! "' + functionString + '"'); + assert.strictEqual(typeof f, 'function', name + ' should be loaded as a function'); + + assert.deepEqual(inputs.map(f), expectedOutputs, + name + ' should be loaded correctly'); + + assert.deepEqual(inputs.map(loadAfterDump(f)), expectedOutputs, + name + ' should be dumped then loaded correctly'); + } + + // Backslash-escapes double quotes and newlines. + function escapeFnString(s) { + return s.replace(/"/g, '\\"').replace(/\n/g, '\\n'); + } + + var fnFactorial = escapeFnString( + 'function factorial(start) {\n' + + '// Non-indented long line to trigger folding: throw new Error("bad fold"); throw new Error("bad fold");\n' + + ' var extra_long_string = "try to trick the dumper into creating a syntax error by folding this string";\n' + + ' var extra_long_string1 = "try to trick the dumper into creating a syntax error by folding this string";\n' + + 'var extra_long_string2 = "this long string is fine to fold because it is not more-indented";\n' + + 'function fac (n) {\n' + + 'if (n <= 0) return 1; return n * fac(n-1); // here is a long line that can be safely folded\n' + + '}\n' + + 'return fac(start);\n' + + '}\n'); + + var fnCollatz = escapeFnString( + 'function collatz(start) {\n' + + ' var longString = "another long more-indented string that will cause a syntax error if folded";\n' + + 'var result = [];\n' + + 'function go(n) { result.push(n); return (n === 1) ? result : go(n % 2 === 0 ? n / 2 : 3 * n + 1); }\n' + + 'return go(start >= 1 ? Math.floor(start) : 1);\n' + + '}'); + + var fnRot13 = escapeFnString( + // single-line function. + // note the "{return" is so the line doesn't start with a space. + 'function rot13(s) {return String.fromCharCode.apply(null, s.split("")' + + '.map(function (c) { return ((c.toLowerCase().charCodeAt(0) - 97) + 13) % 26 + 97; })); }' + ); + + assertFunctionPreserved(fnFactorial, + [ 0, 1, 2, 3, 5, 7, 12 ], + [ 1, 1, 2, 6, 120, 5040, 479001600 ], + 'Factorial function'); + + assertFunctionPreserved(fnCollatz, + [ 6, 19 ], + [ [ 6, 3, 10, 5, 16, 8, 4, 2, 1 ], + [ 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 ] + ], 'Hailstone sequence function'); + + assertFunctionPreserved(fnRot13, + [ 'nggnpxngqnja', 'orjnergurvqrfbsznepu' ], + [ 'attackatdawn', 'bewaretheidesofmarch' ], + 'ROT13'); +}); + +test('Folding long regular expressions preserves content', function () { + // Tests loading a regex, then tests dumping and loading. + function assertRegexPreserved(string, stringPattern) { + assert.strictEqual(string.search(stringPattern), 0, + 'The test itself has errors: regex did not match its string'); + + var loadedRe = yaml.load('"key": ! /' + + stringPattern + '/').key; + assert.strictEqual(loadedRe.exec(string)[0], string, + 'Loaded regex did not match the original string'); + + assert.strictEqual( + loadAfterDump({ key: new RegExp(stringPattern) }).key.exec(string)[0], + string, + 'Dumping and loading did not preserve the regex'); + } + + var s1 = 'This is a very long regular expression. ' + + 'It\'s so long that it is longer than 80 characters per line.'; + var s1Pattern = '^This is a very long regular expression\\. ' + + 'It\'s so long that it is longer than 80 characters per line\\.$'; + + assertRegexPreserved(s1, s1Pattern); +}); + +test('Strings are folded as usual', function () { + var doc = yaml.load('"key": |\n It is just a very long string. It should be folded because the dumper ' + + 'fold lines that are exceed limit in 80 characters per line.'); + var dump = yaml.dump(doc); + assert(Math.max.apply(null, dump.split('\n').map(function (str) { return str.length; })) <= 80); +}); diff --git a/encoding/yaml/__test__/issues/0220.js b/encoding/yaml/__test__/issues/0220.js new file mode 100644 index 000000000000..9693b6ab4421 --- /dev/null +++ b/encoding/yaml/__test__/issues/0220.js @@ -0,0 +1,15 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Float type dumper should not miss dot', function () { + assert.strictEqual(5e-100.toString(10), '5e-100'); + assert.strictEqual(0.5e-100.toString(10), '5e-101'); + + assert.strictEqual(yaml.dump(0.5e-100), '5.e-101\n'); + assert.strictEqual(yaml.load(yaml.dump(5e-100)), 5e-100); +}); diff --git a/encoding/yaml/__test__/issues/0221.js b/encoding/yaml/__test__/issues/0221.js new file mode 100644 index 000000000000..46d71a1274fd --- /dev/null +++ b/encoding/yaml/__test__/issues/0221.js @@ -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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test.skip('Block scalar chomping does not work on zero indent', function () { + assert.throws(function () { yaml.load('|-\nfoo\nbar'); }, yaml.YAMLException); + assert.deepEqual(yaml.dump('foo\nbar'), '|-\n foo\nbar'); +}); diff --git a/encoding/yaml/__test__/issues/0235.js b/encoding/yaml/__test__/issues/0235.js new file mode 100644 index 000000000000..be630872e70f --- /dev/null +++ b/encoding/yaml/__test__/issues/0235.js @@ -0,0 +1,15 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + +test('Flow style does not dump with block literals.', function () { + assert.strictEqual(yaml.dump({ a: '\n' }, { flowLevel: 0 }), '{a: "\\n"}\n'); +}); + +test('Ok to dump block-style literals when not yet flowing.', function () { + // cf. example 8.6 from the YAML 1.2 spec + assert.strictEqual(yaml.dump({ a: '\n' }, { flowLevel: 2 }), 'a: |+\n\n'); +}); diff --git a/encoding/yaml/__test__/issues/0243-basic.yml b/encoding/yaml/__test__/issues/0243-basic.yml new file mode 100644 index 000000000000..ee98211d11b0 --- /dev/null +++ b/encoding/yaml/__test__/issues/0243-basic.yml @@ -0,0 +1,15 @@ +a: + b: 1 + c: 2 +d: + e: 3 + f: 4 +duplicate: # 1 + id: 1234 + note: "this is the first one, so it won't raise an error" +duplicate: # 2 + id: 5678 + note: "this is the second one, so it will raise an error, hopefully at the start of the key" +g: + h: 5 + i: 6 diff --git a/encoding/yaml/__test__/issues/0243-nested.yml b/encoding/yaml/__test__/issues/0243-nested.yml new file mode 100644 index 000000000000..b24c33b18080 --- /dev/null +++ b/encoding/yaml/__test__/issues/0243-nested.yml @@ -0,0 +1,15 @@ +a: + b: 1 + c: 2 +d: + e: 3 + f: 4 + duplicate: # 1 + id: 1234 + note: "this is the first one, so it won't raise an error" + duplicate: # 2 + id: 5678 + note: "this is the second one, so it will raise an error, hopefully at the start of the key" +g: + h: 5 + i: 6 diff --git a/encoding/yaml/__test__/issues/0243.js b/encoding/yaml/__test__/issues/0243.js new file mode 100644 index 000000000000..5d320dd104a2 --- /dev/null +++ b/encoding/yaml/__test__/issues/0243.js @@ -0,0 +1,30 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Duplicated mapping key errors on top level throw at beginning of key', function () { + var src = readFileSync(require('path').join(__dirname, '/0243-basic.yml'), 'utf8'); + var lines = src.split('\n'); + + try { + yaml.safeLoad(src); + } catch (e) { + assert.equal(lines[e.mark.line], 'duplicate: # 2'); + } +}); + +test('Duplicated mapping key errors inside of mapping values throw at beginning of key', function () { + var src = readFileSync(require('path').join(__dirname, '/0243-nested.yml'), 'utf8'); + var lines = src.split('\n'); + + try { + yaml.safeLoad(src); + } catch (e) { + assert.equal(lines[e.mark.line], ' duplicate: # 2'); + } +}); diff --git a/encoding/yaml/__test__/issues/0248-listener.js b/encoding/yaml/__test__/issues/0248-listener.js new file mode 100644 index 000000000000..f8aa101c6d38 --- /dev/null +++ b/encoding/yaml/__test__/issues/0248-listener.js @@ -0,0 +1,64 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + +test('Listener informed on a very simple scalar.', function () { + var history = []; + function l(eventType, state) { + history.push([ eventType, state.position ]); + } + + yaml.load('a_simple_scalar', { listener: l }); + + // 2 open events then 2 close events + assert.strictEqual(history.length, 4); + assert.strictEqual(history[0][0], 'open'); + assert.strictEqual(history[1][0], 'open'); + assert.strictEqual(history[2][0], 'close'); + assert.strictEqual(history[3][0], 'close'); + assert.strictEqual(history[0][1], 0); + assert.strictEqual(history[3][1], 16); +}); + +test('Listener informed on a map with a list.', function () { + var history = []; + function l(eventType, state) { + history.push([ eventType, state.position, state.result ]); + } + + yaml.load('{ a: 1, b: [ 0, xyz ] }', { listener: l }); + + var i = -1; + assert.strictEqual(history[++i][0], 'open'); // doc + assert.strictEqual(history[++i][0], 'open'); // map + + assert.strictEqual(history[++i][0], 'open'); // key + assert.strictEqual(history[++i][0], 'close'); + assert.strictEqual(history[i][2], 'a'); + + assert.strictEqual(history[++i][0], 'open'); // a value + assert.strictEqual(history[++i][0], 'close'); + assert.strictEqual(history[i][2], 1); + + assert.strictEqual(history[++i][0], 'open'); // key + assert.strictEqual(history[++i][0], 'close'); + assert.strictEqual(history[i][2], 'b'); + + assert.strictEqual(history[++i][0], 'open'); // b value (list) + assert.strictEqual(history[++i][0], 'open'); // item in list + assert.strictEqual(history[++i][0], 'close'); + assert.strictEqual(history[i][2], 0); + assert.strictEqual(history[++i][0], 'open'); // item in list + assert.strictEqual(history[++i][0], 'close'); + + assert.strictEqual(history[++i][0], 'close'); // b value (list) end + assert.deepEqual(history[i][2], [ 0, 'xyz' ]); + + assert.strictEqual(history[++i][0], 'close'); // map end + assert.strictEqual(history[++i][0], 'close'); // doc end + + assert.strictEqual(history.length, ++i); +}); diff --git a/encoding/yaml/__test__/issues/0266.js b/encoding/yaml/__test__/issues/0266.js new file mode 100644 index 000000000000..f6abfedec698 --- /dev/null +++ b/encoding/yaml/__test__/issues/0266.js @@ -0,0 +1,24 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +var DEPRECATED_BOOLEANS_SYNTAX = [ + 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON', + 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF' +]; + + +test('Dumper should not take into account booleans syntax from YAML 1.0/1.1 in noCompatMode', function () { + DEPRECATED_BOOLEANS_SYNTAX.forEach(function (string) { + var dump = yaml.dump(string, { noCompatMode: true }).trim(); + + assert( + (dump === string), + ('"' + string + '" string is not dumped as-is; actual dump: ' + dump) + ); + }); +}); diff --git a/encoding/yaml/__test__/issues/0303.js b/encoding/yaml/__test__/issues/0303.js new file mode 100644 index 000000000000..13df7fbe98ff --- /dev/null +++ b/encoding/yaml/__test__/issues/0303.js @@ -0,0 +1,13 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + +test('Loader should not strip quotes before newlines', function () { + var with_space = yaml.load("'''foo'' '"); + var with_newline = yaml.load("'''foo''\n'"); + assert.strictEqual(with_space, "'foo' "); + assert.strictEqual(with_newline, "'foo' "); +}); diff --git a/encoding/yaml/__test__/issues/0333.js b/encoding/yaml/__test__/issues/0333.js new file mode 100644 index 000000000000..3b4b5bbc4ffd --- /dev/null +++ b/encoding/yaml/__test__/issues/0333.js @@ -0,0 +1,18 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('should allow cast integers as !!float', function () { + var data = yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0333.yml'), 'utf8')); + + assert.deepEqual(data, { + negative: -1, + zero: 0, + positive: 23000 + }); +}); diff --git a/encoding/yaml/__test__/issues/0333.yml b/encoding/yaml/__test__/issues/0333.yml new file mode 100644 index 000000000000..6ff266236e12 --- /dev/null +++ b/encoding/yaml/__test__/issues/0333.yml @@ -0,0 +1,3 @@ +negative: !!float -1 +zero: !!float 0 +positive: !!float 2.3e4 diff --git a/encoding/yaml/__test__/issues/0335.js b/encoding/yaml/__test__/issues/0335.js new file mode 100644 index 000000000000..4e35b2023a62 --- /dev/null +++ b/encoding/yaml/__test__/issues/0335.js @@ -0,0 +1,21 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Don\'t throw on warning', function () { + var src = readFileSync(require('path').join(__dirname, '/0335.yml'), 'utf8'); + + assert.deepEqual(yaml.safeLoad(src), { + not_num_1: '-_123', + not_num_2: '_123', + not_num_3: '123_', + not_num_4: '0b00_', + not_num_5: '0x00_', + not_num_6: '011_' + }); +}); diff --git a/encoding/yaml/__test__/issues/0335.yml b/encoding/yaml/__test__/issues/0335.yml new file mode 100644 index 000000000000..a4b30b6c3c5e --- /dev/null +++ b/encoding/yaml/__test__/issues/0335.yml @@ -0,0 +1,6 @@ +not_num_1: -_123 +not_num_2: _123 +not_num_3: 123_ +not_num_4: 0b00_ +not_num_5: 0x00_ +not_num_6: 011_ diff --git a/encoding/yaml/__test__/issues/0342.js b/encoding/yaml/__test__/issues/0342.js new file mode 100644 index 000000000000..651530c75c30 --- /dev/null +++ b/encoding/yaml/__test__/issues/0342.js @@ -0,0 +1,62 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var simpleArray = [ 'a', 'b' ]; +var arrayOfSimpleObj = [ { a: 1 }, { b: 2 } ]; +var arrayOfObj = [ { a: 1, b: 'abc' }, { c: 'def', d: 2 } ]; + + +test('space should be added for array, regardless of indent', function () { + assert.deepEqual( + yaml.dump(simpleArray, { indent: 1 }), + '- a\n- b\n' + ); + assert.deepEqual( + yaml.dump(simpleArray, { indent: 2 }), + '- a\n- b\n' + ); + assert.deepEqual( + yaml.dump(simpleArray, { indent: 3 }), + '- a\n- b\n' + ); + assert.deepEqual( + yaml.dump(simpleArray, { indent: 4 }), + '- a\n- b\n' + ); +}); + +test('array of objects should not wrap at indentation of 2', function () { + assert.deepEqual( + yaml.dump(arrayOfSimpleObj, { indent: 2 }), + '- a: 1\n- b: 2\n' + ); + assert.deepEqual( + yaml.dump(arrayOfObj, { indent: 2 }), + '- a: 1\n b: abc\n- c: def\n d: 2\n' + ); +}); + +test('EOL space should not be added on array of objects at indentation of 3', function () { + assert.deepEqual( + yaml.dump(arrayOfSimpleObj, { indent: 3 }), + '-\n a: 1\n-\n b: 2\n' + ); + assert.deepEqual( + yaml.dump(arrayOfObj, { indent: 3 }), + '-\n a: 1\n b: abc\n-\n c: def\n d: 2\n' + ); +}); + +test('EOL space should not be added on array of objects at indentation of 4', function () { + assert.deepEqual( + yaml.dump(arrayOfSimpleObj, { indent: 4 }), + '-\n a: 1\n-\n b: 2\n' + ); + assert.deepEqual( + yaml.dump(arrayOfObj, { indent: 4 }), + '-\n a: 1\n b: abc\n-\n c: def\n d: 2\n' + ); +}); diff --git a/encoding/yaml/__test__/issues/0346.js b/encoding/yaml/__test__/issues/0346.js new file mode 100644 index 000000000000..e0179ff59a46 --- /dev/null +++ b/encoding/yaml/__test__/issues/0346.js @@ -0,0 +1,27 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('should not emit spaces in arrays in flow mode between entries using condenseFlow: true', function () { + var array = [ 'a', 'b' ]; + var dumpedArray = yaml.dump(array, { flowLevel: 0, indent: 0, condenseFlow: true }); + assert.equal( + dumpedArray, + '[a,b]\n' + ); + assert.deepEqual(yaml.load(dumpedArray), array); +}); + +test('should not emit spaces between key: value and quote keys using condenseFlow: true', function () { + var object = { a: { b: 'c' } }; + var objectDump = yaml.dump(object, { flowLevel: 0, indent: 0, condenseFlow: true }); + assert.equal( + objectDump, + '{"a":{"b":c}}\n' + ); + assert.deepEqual(yaml.load(objectDump), object); +}); diff --git a/encoding/yaml/__test__/issues/0350.js b/encoding/yaml/__test__/issues/0350.js new file mode 100644 index 000000000000..7386fbced3a3 --- /dev/null +++ b/encoding/yaml/__test__/issues/0350.js @@ -0,0 +1,14 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('should return parse docs from loadAll', function () { + var data = yaml.safeLoadAll(readFileSync(require('path').join(__dirname, '/0350.yml'), 'utf8')); + + assert.deepEqual(data, [ { a: 1 }, { b: 2 } ]); +}); diff --git a/encoding/yaml/__test__/issues/0350.yml b/encoding/yaml/__test__/issues/0350.yml new file mode 100644 index 000000000000..d3f18ac45ba4 --- /dev/null +++ b/encoding/yaml/__test__/issues/0350.yml @@ -0,0 +1,4 @@ +--- +a: 1 +--- +b: 2 diff --git a/encoding/yaml/__test__/issues/0351.js b/encoding/yaml/__test__/issues/0351.js new file mode 100644 index 000000000000..5e956bdaf232 --- /dev/null +++ b/encoding/yaml/__test__/issues/0351.js @@ -0,0 +1,18 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../..'); +var readFileSync = require('fs').readFileSync; + + +test('should include the error message in the error stack', function () { + try { + yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0351.yml'), 'utf8')); + } catch (err) { + assert(err.stack.startsWith('YAMLException: end of the stream or a document separator is expected')); + return; + } + assert.fail(null, null, 'Expected an error to be thrown'); +}); diff --git a/encoding/yaml/__test__/issues/0351.yml b/encoding/yaml/__test__/issues/0351.yml new file mode 100644 index 000000000000..8ab27b9aac66 --- /dev/null +++ b/encoding/yaml/__test__/issues/0351.yml @@ -0,0 +1,4 @@ +# intentionally invalid yaml + + foo: bar +baz: qux diff --git a/encoding/yaml/__test__/issues/0369.js b/encoding/yaml/__test__/issues/0369.js new file mode 100644 index 000000000000..7a53003f2adf --- /dev/null +++ b/encoding/yaml/__test__/issues/0369.js @@ -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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('should dump astrals as codepoint', function () { + assert.deepEqual(yaml.safeDump('😀'), '"\\U0001F600"\n'); + assert.deepEqual(yaml.safeLoad('"\\U0001F600"'), '😀'); +}); diff --git a/encoding/yaml/__test__/issues/0399.js b/encoding/yaml/__test__/issues/0399.js new file mode 100644 index 000000000000..df28ad99d8a1 --- /dev/null +++ b/encoding/yaml/__test__/issues/0399.js @@ -0,0 +1,20 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('should properly dump negative ints in different styles', function () { + var dump, src = { integer: -100 }; + + dump = yaml.dump(src, { styles: { '!!int': 'binary' } }); + assert.deepEqual(yaml.safeLoad(dump), src); + + dump = yaml.dump(src, { styles: { '!!int': 'octal' } }); + assert.deepEqual(yaml.safeLoad(dump), src); + + dump = yaml.dump(src, { styles: { '!!int': 'hex' } }); + assert.deepEqual(yaml.safeLoad(dump), src); +}); diff --git a/encoding/yaml/__test__/issues/0403.js b/encoding/yaml/__test__/issues/0403.js new file mode 100644 index 000000000000..2b1362d566b7 --- /dev/null +++ b/encoding/yaml/__test__/issues/0403.js @@ -0,0 +1,23 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('should properly dump leading newlines and spaces', function () { + var dump, src; + + src = { str: '\n a\nb' }; + dump = yaml.dump(src); + assert.deepEqual(yaml.safeLoad(dump), src); + + src = { str: '\n\n a\nb' }; + dump = yaml.dump(src); + assert.deepEqual(yaml.safeLoad(dump), src); + + src = { str: '\n a\nb' }; + dump = yaml.dump(src, { indent: 10 }); + assert.deepEqual(yaml.safeLoad(dump), src); +}); diff --git a/encoding/yaml/__test__/issues/0432.js b/encoding/yaml/__test__/issues/0432.js new file mode 100644 index 000000000000..ac525c9879c7 --- /dev/null +++ b/encoding/yaml/__test__/issues/0432.js @@ -0,0 +1,19 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('should indent arrays an extra level by default', function () { + var output = yaml.safeDump({ array: [ 'a', 'b' ] }); + var expected = 'array:\n - a\n - b\n'; + assert.strictEqual(output, expected); +}); + +test('should not indent arrays an extra level when disabled', function () { + var output = yaml.safeDump({ array: [ 'a', 'b' ] }, { noArrayIndent: true }); + var expected = 'array:\n- a\n- b\n'; + assert.strictEqual(output, expected); +}); diff --git a/encoding/yaml/__test__/issues/0468.js b/encoding/yaml/__test__/issues/0468.js new file mode 100644 index 000000000000..ef13cda9b849 --- /dev/null +++ b/encoding/yaml/__test__/issues/0468.js @@ -0,0 +1,30 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../..'); + +test('should not indent arrays an extra level when disabled', function () { + /* eslint-disable max-len */ + var output = yaml.dump( + [ + { + a: 'a_val', + b: 'b_val' + }, + { + a: 'a2_val', + items: [ + { + a: 'a_a_val', + b: 'a_b_val' + } + ] + } + ], + { noArrayIndent: true } + ); + var expected = '- a: a_val\n b: b_val\n- a: a2_val\n items:\n - a: a_a_val\n b: a_b_val\n'; + assert.strictEqual(output, expected); +}); diff --git a/encoding/yaml/__test__/issues/0475-case1.yml b/encoding/yaml/__test__/issues/0475-case1.yml new file mode 100644 index 000000000000..29e78476f017 --- /dev/null +++ b/encoding/yaml/__test__/issues/0475-case1.yml @@ -0,0 +1,117 @@ +? - - &id057 + - &id055 + - &id053 + - &id051 + - &id049 + - &id047 + - &id045 + - &id043 + - &id041 + - &id039 + - &id037 + - &id035 + - &id033 + - &id031 + - &id029 + - &id027 + - &id025 + - &id023 + - &id021 + - &id019 + - &id017 + - &id015 + - &id013 + - &id011 + - &id009 + - &id007 + - &id005 + - &id003 + - &id001 [lol] + - &id002 [lol] + - &id004 + - *id001 + - *id002 + - &id006 + - *id003 + - *id004 + - &id008 + - *id005 + - *id006 + - &id010 + - *id007 + - *id008 + - &id012 + - *id009 + - *id010 + - &id014 + - *id011 + - *id012 + - &id016 + - *id013 + - *id014 + - &id018 + - *id015 + - *id016 + - &id020 + - *id017 + - *id018 + - &id022 + - *id019 + - *id020 + - &id024 + - *id021 + - *id022 + - &id026 + - *id023 + - *id024 + - &id028 + - *id025 + - *id026 + - &id030 + - *id027 + - *id028 + - &id032 + - *id029 + - *id030 + - &id034 + - *id031 + - *id032 + - &id036 + - *id033 + - *id034 + - &id038 + - *id035 + - *id036 + - &id040 + - *id037 + - *id038 + - &id042 + - *id039 + - *id040 + - &id044 + - *id041 + - *id042 + - &id046 + - *id043 + - *id044 + - &id048 + - *id045 + - *id046 + - &id050 + - *id047 + - *id048 + - &id052 + - *id049 + - *id050 + - &id054 + - *id051 + - *id052 + - &id056 + - *id053 + - *id054 + - &id058 + - *id055 + - *id056 + - - *id057 + - *id058 +: key diff --git a/encoding/yaml/__test__/issues/0475-case2.yml b/encoding/yaml/__test__/issues/0475-case2.yml new file mode 100644 index 000000000000..f5c69b535b21 --- /dev/null +++ b/encoding/yaml/__test__/issues/0475-case2.yml @@ -0,0 +1,112 @@ + - &id057 + - &id055 + - &id053 + - &id051 + - &id049 + - &id047 + - &id045 + - &id043 + - &id041 + - &id039 + - &id037 + - &id035 + - &id033 + - &id031 + - &id029 + - &id027 + - &id025 + - &id023 + - &id021 + - &id019 + - &id017 + - &id015 + - &id013 + - &id011 + - &id009 + - &id007 + - &id005 + - &id003 + - &id001 [lol] + - &id002 [lol] + - &id004 + - *id001 + - *id002 + - &id006 + - *id003 + - *id004 + - &id008 + - *id005 + - *id006 + - &id010 + - *id007 + - *id008 + - &id012 + - *id009 + - *id010 + - &id014 + - *id011 + - *id012 + - &id016 + - *id013 + - *id014 + - &id018 + - *id015 + - *id016 + - &id020 + - *id017 + - *id018 + - &id022 + - *id019 + - *id020 + - &id024 + - *id021 + - *id022 + - &id026 + - *id023 + - *id024 + - &id028 + - *id025 + - *id026 + - &id030 + - *id027 + - *id028 + - &id032 + - *id029 + - *id030 + - &id034 + - *id031 + - *id032 + - &id036 + - *id033 + - *id034 + - &id038 + - *id035 + - *id036 + - &id040 + - *id037 + - *id038 + - &id042 + - *id039 + - *id040 + - &id044 + - *id041 + - *id042 + - &id046 + - *id043 + - *id044 + - &id048 + - *id045 + - *id046 + - &id050 + - *id047 + - *id048 + - &id052 + - *id049 + - *id050 + - &id054 + - *id051 + - *id052 + - &id056 + - *id053 + - *id054 + - *id057 : 1 diff --git a/encoding/yaml/__test__/issues/0475.js b/encoding/yaml/__test__/issues/0475.js new file mode 100644 index 000000000000..8eb5afa50093 --- /dev/null +++ b/encoding/yaml/__test__/issues/0475.js @@ -0,0 +1,28 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Should not allow nested arrays in map keys (explicit syntax)', function () { + try { + yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0475-case1.yml'), 'utf8')); + } catch (err) { + assert(err.stack.startsWith('YAMLException: nested arrays are not supported inside keys')); + return; + } + assert.fail(null, null, 'Expected an error to be thrown'); +}); + +test('Should not allow nested arrays in map keys (implicit syntax)', function () { + try { + yaml.safeLoad(readFileSync(require('path').join(__dirname, '/0475-case2.yml'), 'utf8')); + } catch (err) { + assert(err.stack.startsWith('YAMLException: nested arrays are not supported inside keys')); + return; + } + assert.fail(null, null, 'Expected an error to be thrown'); +}); diff --git a/encoding/yaml/__test__/issues/0480-date.yml b/encoding/yaml/__test__/issues/0480-date.yml new file mode 100644 index 000000000000..3fcac6e08ea4 --- /dev/null +++ b/encoding/yaml/__test__/issues/0480-date.yml @@ -0,0 +1 @@ +{ ! '2019-04-05T12:00:43.467Z': 123 } diff --git a/encoding/yaml/__test__/issues/0480-fn-array.yml b/encoding/yaml/__test__/issues/0480-fn-array.yml new file mode 100644 index 000000000000..2e151bf5a172 --- /dev/null +++ b/encoding/yaml/__test__/issues/0480-fn-array.yml @@ -0,0 +1,4 @@ +? [ + 123, + { toString: ! 'function (){throw new Error("code execution")}' } +] : key diff --git a/encoding/yaml/__test__/issues/0480-fn.yml b/encoding/yaml/__test__/issues/0480-fn.yml new file mode 100644 index 000000000000..68412be544a1 --- /dev/null +++ b/encoding/yaml/__test__/issues/0480-fn.yml @@ -0,0 +1 @@ +{ toString: ! 'function (){throw new Error("code execution")}' } : key diff --git a/encoding/yaml/__test__/issues/0480-fn2.yml b/encoding/yaml/__test__/issues/0480-fn2.yml new file mode 100644 index 000000000000..6efd250d141f --- /dev/null +++ b/encoding/yaml/__test__/issues/0480-fn2.yml @@ -0,0 +1 @@ +{ __proto__: { toString: ! 'function(){throw new Error("code execution")}' } } : key diff --git a/encoding/yaml/__test__/issues/0480.js b/encoding/yaml/__test__/issues/0480.js new file mode 100644 index 000000000000..b0e36381af97 --- /dev/null +++ b/encoding/yaml/__test__/issues/0480.js @@ -0,0 +1,35 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); +var readFileSync = require('fs').readFileSync; + + +test('Should not execute code when object with toString property is used as a key', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-fn.yml'), 'utf8')); + + assert.deepEqual(data, { '[object Object]': 'key' }); +}); + +test('Should not execute code when object with __proto__ property is used as a key', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-fn2.yml'), 'utf8')); + + assert.deepEqual(data, { '[object Object]': 'key' }); +}); + +test('Should not execute code when object inside array is used as a key', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-fn-array.yml'), 'utf8')); + + assert.deepEqual(data, { '123,[object Object]': 'key' }); +}); + +// this test does not guarantee in any way proper handling of date objects, +// it just keeps old behavior whenever possible +test('Should leave non-plain objects as is', function () { + var data = yaml.load(readFileSync(require('path').join(__dirname, '/0480-date.yml'), 'utf8')); + + assert.deepEqual(Object.keys(data).length, 1); + assert(/2019/.test(Object.keys(data)[0])); +}); diff --git a/encoding/yaml/__test__/samples-common/construct-binary.js b/encoding/yaml/__test__/samples-common/construct-binary.js new file mode 100644 index 000000000000..646d438b3d36 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-binary.js @@ -0,0 +1,18 @@ +// 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. + +/*eslint-disable max-len*/ + +// Support node 6.+ Buffer API when available +function buffer(data, encoding) { + return Buffer.from ? Buffer.from(data, encoding) : new Buffer(data, encoding); +} + +module.exports = { + canonical: buffer("GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;", 'binary'), + + generic: buffer("GIF89a\x0c\x00\x0c\x00\x84\x00\x00\xff\xff\xf7\xf5\xf5\xee\xe9\xe9\xe5fff\x00\x00\x00\xe7\xe7\xe7^^^\xf3\xf3\xed\x8e\x8e\x8e\xe0\xe0\xe0\x9f\x9f\x9f\x93\x93\x93\xa7\xa7\xa7\x9e\x9e\x9eiiiccc\xa3\xa3\xa3\x84\x84\x84\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9\xff\xfe\xf9!\xfe\x0eMade with GIMP\x00,\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x05, \x8e\x810\x9e\xe3@\x14\xe8i\x10\xc4\xd1\x8a\x08\x1c\xcf\x80M$z\xef\xff0\x85p\xb8\xb01f\r\x1b\xce\x01\xc3\x01\x1e\x10' \x82\n\x01\x00;", 'binary'), + + description: 'The binary value above is a tiny arrow encoded as a gif image.' +}; diff --git a/encoding/yaml/__test__/samples-common/construct-binary.yml b/encoding/yaml/__test__/samples-common/construct-binary.yml new file mode 100644 index 000000000000..dcdb16f3b4ef --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-binary.yml @@ -0,0 +1,12 @@ +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. diff --git a/encoding/yaml/__test__/samples-common/construct-bool.js b/encoding/yaml/__test__/samples-common/construct-bool.js new file mode 100644 index 000000000000..975088e23713 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-bool.js @@ -0,0 +1,10 @@ +// 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. + +module.exports = { + valid_true: [ true, true, true ], + valid_false: [ false, false, false ], + deprecated_true: [ 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON' ], + deprecated_false: [ 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF' ] +}; diff --git a/encoding/yaml/__test__/samples-common/construct-bool.yml b/encoding/yaml/__test__/samples-common/construct-bool.yml new file mode 100644 index 000000000000..2971e0a5194b --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-bool.yml @@ -0,0 +1,33 @@ +# Valid notation. (YAML 1.2) +# +valid_true: + - true + - True + - TRUE + +valid_false: + - false + - False + - FALSE + +# Deprecated notation. (YAML 1.0/1.1) +# +deprecated_true: + - y + - Y + - yes + - Yes + - YES + - on + - On + - ON + +deprecated_false: + - n + - N + - no + - No + - NO + - off + - Off + - OFF \ No newline at end of file diff --git a/encoding/yaml/__test__/samples-common/construct-custom.js b/encoding/yaml/__test__/samples-common/construct-custom.js new file mode 100644 index 000000000000..e02c1373064a --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-custom.js @@ -0,0 +1,46 @@ +// 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. + +var assert = require('assert'); +var schema = require('../support/schema'); + +var expected = [ + new schema.Tag1({ x: 1 }), + new schema.Tag1({ x: 1, y: 2, z: 3 }), + new schema.Tag2({ x: 10 }), + new schema.Tag3({ x: 1 }), + new schema.Tag3({ x: 1, y: 2, z: 3 }), + new schema.Tag3({ x: 1, y: 2, z: 3 }), + new schema.Foo({ myParameter: 'foo', myAnotherParameter: [ 1, 2, 3 ] }) +]; + +function testHandler(actual) { + assert.strictEqual(Object.prototype.toString.call(actual), '[object Array]'); + assert.strictEqual(actual.length, 7); + + assert.deepEqual(actual[0], expected[0]); + assert.strictEqual(Object.getPrototypeOf(actual[0]), Object.getPrototypeOf(expected[0])); + + assert.deepEqual(actual[1], expected[1]); + assert.strictEqual(Object.getPrototypeOf(actual[1]), Object.getPrototypeOf(expected[1])); + + assert.deepEqual(actual[2], expected[2]); + assert.strictEqual(Object.getPrototypeOf(actual[2]), Object.getPrototypeOf(expected[2])); + + assert.deepEqual(actual[3], expected[3]); + assert.strictEqual(Object.getPrototypeOf(actual[3]), Object.getPrototypeOf(expected[3])); + + assert.deepEqual(actual[4], expected[4]); + assert.strictEqual(Object.getPrototypeOf(actual[4]), Object.getPrototypeOf(expected[4])); + + assert.deepEqual(actual[5], expected[5]); + assert.strictEqual(Object.getPrototypeOf(actual[5]), Object.getPrototypeOf(expected[5])); + + assert.deepEqual(actual[6], expected[6]); + assert.strictEqual(Object.getPrototypeOf(actual[6]), Object.getPrototypeOf(expected[6])); +} + +testHandler.expected = expected; + +module.exports = testHandler; diff --git a/encoding/yaml/__test__/samples-common/construct-custom.yml b/encoding/yaml/__test__/samples-common/construct-custom.yml new file mode 100644 index 000000000000..7144683225b5 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-custom.yml @@ -0,0 +1,22 @@ +--- +- !tag1 + x: 1 +- !tag1 + x: 1 + 'y': 2 + z: 3 +- !tag2 + 10 +- !tag3 + x: 1 +- !tag3 + x: 1 + 'y': 2 + z: 3 +- !tag3 + =: 1 + 'y': 2 + z: 3 +- !foo + my-parameter: foo + my-another-parameter: [1,2,3] diff --git a/encoding/yaml/__test__/samples-common/construct-float.js b/encoding/yaml/__test__/samples-common/construct-float.js new file mode 100644 index 000000000000..38c697d1bc1d --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-float.js @@ -0,0 +1,31 @@ +// 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. + +var assert = require('assert'); + +var expected = { + canonical: 685230.15, + exponential: 685230.15, + fixed: 685230.15, + sexagesimal: 685230.15, + 'negative infinity': Number.NEGATIVE_INFINITY, + 'not a number': NaN +}; + +function testHandler(actual) { + assert.strictEqual(Object.prototype.toString.call(actual), '[object Object]'); + assert.strictEqual(Object.keys(actual).sort().join(','), Object.keys(expected).sort().join(',')); + + assert.strictEqual(actual['canonical'], expected['canonical']); + assert.strictEqual(actual['exponential'], expected['exponential']); + assert.strictEqual(actual['fixed'], expected['fixed']); + assert.strictEqual(actual['sexagesimal'], expected['sexagesimal']); + assert.strictEqual(actual['negative infinity'], expected['negative infinity']); + + assert(Number.isNaN(actual['not a number'])); +} + +testHandler.expected = expected; + +module.exports = testHandler; diff --git a/encoding/yaml/__test__/samples-common/construct-float.yml b/encoding/yaml/__test__/samples-common/construct-float.yml new file mode 100644 index 000000000000..b662c6236283 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-float.yml @@ -0,0 +1,6 @@ +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 diff --git a/encoding/yaml/__test__/samples-common/construct-int.js b/encoding/yaml/__test__/samples-common/construct-int.js new file mode 100644 index 000000000000..39027cb05886 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-int.js @@ -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. + +module.exports = { + canonical: 685230, + decimal: 685230, + octal: 685230, + hexadecimal: 685230, + binary: 685230, + sexagesimal: 685230 +}; diff --git a/encoding/yaml/__test__/samples-common/construct-int.yml b/encoding/yaml/__test__/samples-common/construct-int.yml new file mode 100644 index 000000000000..852c3148eed8 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-int.yml @@ -0,0 +1,6 @@ +canonical: 685230 +decimal: +685_230 +octal: 02472256 +hexadecimal: 0x_0A_74_AE +binary: 0b1010_0111_0100_1010_1110 +sexagesimal: 190:20:30 diff --git a/encoding/yaml/__test__/samples-common/construct-javascript-function.js b/encoding/yaml/__test__/samples-common/construct-javascript-function.js new file mode 100644 index 000000000000..64e66eb00262 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-javascript-function.js @@ -0,0 +1,53 @@ +// 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. + +var assert = require('assert'); + + +function testHandler(actual) { + var expected = testHandler.expected; + + assert.strictEqual(actual.length, expected.length); + + assert.strictEqual(actual.length, expected.length); + + assert.strictEqual( + actual[0](), + expected[0]()); + + assert.strictEqual( + actual[1](10, 20), + expected[1](10, 20)); + + assert.deepEqual( + actual[2]('book'), + expected[2]('book')); +} + +testHandler.expected = [ + function () { + return 42; + }, + function () { + return 72; + }, + function () { + return 23; + }, + function (x, y) { + return x + y; + }, + function (foo) { + var result = 'There is my ' + foo + ' at the table.'; + + return { + first: 42, + second: 'sum', + third: result + }; + } +]; + + +module.exports = testHandler; diff --git a/encoding/yaml/__test__/samples-common/construct-javascript-function.yml b/encoding/yaml/__test__/samples-common/construct-javascript-function.yml new file mode 100644 index 000000000000..ca945e2c2300 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-javascript-function.yml @@ -0,0 +1,14 @@ +- !!js/function 'function () { return 42 }' +- !!js/function '() => { return 72 }' +- !!js/function '() => 23' +- !!js/function 'function (x, y) { return x + y; } ' +- !!js/function | + function (foo) { + var result = 'There is my ' + foo + ' at the table.'; + + return { + first: 42, + second: 'sum', + third: result + }; + } \ No newline at end of file diff --git a/encoding/yaml/__test__/samples-common/construct-javascript-regexp.js b/encoding/yaml/__test__/samples-common/construct-javascript-regexp.js new file mode 100644 index 000000000000..8d3cfc52c4f2 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-javascript-regexp.js @@ -0,0 +1,11 @@ +// 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. + +module.exports = [ + /fo{2,}/, + /[wv]orlds?/g, + /^spec/im, + /ba+r/, + /ba.z*/gim +]; diff --git a/encoding/yaml/__test__/samples-common/construct-javascript-regexp.yml b/encoding/yaml/__test__/samples-common/construct-javascript-regexp.yml new file mode 100644 index 000000000000..f3ad62f97a8f --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-javascript-regexp.yml @@ -0,0 +1,5 @@ +- !!js/regexp /fo{2,}/ +- !!js/regexp /[wv]orlds?/g +- !!js/regexp /^spec/im +- !!js/regexp '/ba+r/' +- !!js/regexp '/ba.z*/gim' diff --git a/encoding/yaml/__test__/samples-common/construct-javascript-undefined.js b/encoding/yaml/__test__/samples-common/construct-javascript-undefined.js new file mode 100644 index 000000000000..53150b0f029e --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-javascript-undefined.js @@ -0,0 +1,10 @@ +// 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. + +module.exports = [ + undefined, + undefined, + undefined, + undefined +]; diff --git a/encoding/yaml/__test__/samples-common/construct-javascript-undefined.yml b/encoding/yaml/__test__/samples-common/construct-javascript-undefined.yml new file mode 100644 index 000000000000..4df49db96723 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-javascript-undefined.yml @@ -0,0 +1,4 @@ +- !!js/undefined +- !!js/undefined '' +- !!js/undefined 'foobar' +- !!js/undefined hello world diff --git a/encoding/yaml/__test__/samples-common/construct-map.js b/encoding/yaml/__test__/samples-common/construct-map.js new file mode 100644 index 000000000000..b5247c47c78f --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-map.js @@ -0,0 +1,17 @@ +// 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. + +module.exports = { + 'Block style': { + Clark: 'Evans', + Brian: 'Ingerson', + Oren: 'Ben-Kiki' + }, + 'Flow style': { + Clark: 'Evans', + Brian: 'Ingerson', + Oren: 'Ben-Kiki' + }, + 'foo,bar': 'baz' +}; diff --git a/encoding/yaml/__test__/samples-common/construct-map.yml b/encoding/yaml/__test__/samples-common/construct-map.yml new file mode 100644 index 000000000000..74e006f28a4f --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-map.yml @@ -0,0 +1,11 @@ +# Unordered set of key: value pairs. +Block style: !!map + Clark : Evans + Brian : Ingerson + Oren : Ben-Kiki +Flow style: !!map { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki } + +# YAML has no restrictions on key type, so it can be an array +? - foo + - bar +: baz diff --git a/encoding/yaml/__test__/samples-common/construct-merge.js b/encoding/yaml/__test__/samples-common/construct-merge.js new file mode 100644 index 000000000000..33f0754e46b2 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-merge.js @@ -0,0 +1,14 @@ +// 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. + +module.exports = [ + { x: 1, y: 2 }, + { x: 0, y: 2 }, + { r: 10 }, + { r: 1 }, + { x: 1, y: 2, r: 10, label: 'center/big' }, + { x: 1, y: 2, r: 10, label: 'center/big' }, + { x: 1, y: 2, r: 10, label: 'center/big' }, + { x: 1, y: 2, r: 10, label: 'center/big' } +]; diff --git a/encoding/yaml/__test__/samples-common/construct-merge.yml b/encoding/yaml/__test__/samples-common/construct-merge.yml new file mode 100644 index 000000000000..3fdb2e203c26 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-merge.yml @@ -0,0 +1,27 @@ +--- +- &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: center/big + +- # Merge one map + << : *CENTER + r: 10 + label: center/big + +- # Merge multiple maps + << : [ *CENTER, *BIG ] + label: center/big + +- # Override + << : [ *BIG, *LEFT, *SMALL ] + x: 1 + label: center/big diff --git a/encoding/yaml/__test__/samples-common/construct-null.js b/encoding/yaml/__test__/samples-common/construct-null.js new file mode 100644 index 000000000000..32fdbf2cd3cd --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-null.js @@ -0,0 +1,22 @@ +// 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. + +module.exports = [ + null, + { + empty: null, + canonical: null, + english: null, + 'null': 'null key' + }, + { + sparse: [ + null, + '2nd entry', + null, + '4th entry', + null + ] + } +]; diff --git a/encoding/yaml/__test__/samples-common/construct-null.yml b/encoding/yaml/__test__/samples-common/construct-null.yml new file mode 100644 index 000000000000..9ad0344cfea6 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-null.yml @@ -0,0 +1,18 @@ +# A document may be 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 diff --git a/encoding/yaml/__test__/samples-common/construct-omap.js b/encoding/yaml/__test__/samples-common/construct-omap.js new file mode 100644 index 000000000000..2d00da65f295 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-omap.js @@ -0,0 +1,16 @@ +// 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. + +module.exports = { + Bestiary: [ + { aardvark : 'African pig-like ant eater. Ugly.' }, + { anteater : 'South-American ant eater. Two species.' }, + { anaconda : 'South-American constrictor snake. Scaly.' } + ], + Numbers: [ + { one : 1 }, + { two : 2 }, + { three : 3 } + ] +}; diff --git a/encoding/yaml/__test__/samples-common/construct-omap.yml b/encoding/yaml/__test__/samples-common/construct-omap.yml new file mode 100644 index 000000000000..4fa0f45f26ff --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-omap.yml @@ -0,0 +1,8 @@ +# 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 ] diff --git a/encoding/yaml/__test__/samples-common/construct-pairs.js b/encoding/yaml/__test__/samples-common/construct-pairs.js new file mode 100644 index 000000000000..1ea1ec4410a0 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-pairs.js @@ -0,0 +1,16 @@ +// 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. + +module.exports = { + 'Block tasks': [ + [ 'meeting', 'with team.' ], + [ 'meeting', 'with boss.' ], + [ 'break', 'lunch.' ], + [ 'meeting', 'with client.' ] + ], + 'Flow tasks': [ + [ 'meeting', 'with team' ], + [ 'meeting', 'with boss' ] + ] +}; diff --git a/encoding/yaml/__test__/samples-common/construct-pairs.yml b/encoding/yaml/__test__/samples-common/construct-pairs.yml new file mode 100644 index 000000000000..05f55b942606 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-pairs.yml @@ -0,0 +1,7 @@ +# 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 ] diff --git a/encoding/yaml/__test__/samples-common/construct-seq.js b/encoding/yaml/__test__/samples-common/construct-seq.js new file mode 100644 index 000000000000..f36f02293fcb --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-seq.js @@ -0,0 +1,28 @@ +// 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. + +module.exports = { + 'Block style': [ + 'Mercury', + 'Venus', + 'Earth', + 'Mars', + 'Jupiter', + 'Saturn', + 'Uranus', + 'Neptune', + 'Pluto' + ], + 'Flow style': [ + 'Mercury', + 'Venus', + 'Earth', + 'Mars', + 'Jupiter', + 'Saturn', + 'Uranus', + 'Neptune', + 'Pluto' + ] +}; diff --git a/encoding/yaml/__test__/samples-common/construct-seq.yml b/encoding/yaml/__test__/samples-common/construct-seq.yml new file mode 100644 index 000000000000..bb92fd11f162 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-seq.yml @@ -0,0 +1,15 @@ +# 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 + diff --git a/encoding/yaml/__test__/samples-common/construct-set.js b/encoding/yaml/__test__/samples-common/construct-set.js new file mode 100644 index 000000000000..803866bf35c5 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-set.js @@ -0,0 +1,16 @@ +// 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. + +module.exports = { + 'baseball players': { + 'Mark McGwire': null, + 'Sammy Sosa': null, + 'Ken Griffey': null + }, + 'baseball teams': { + 'Boston Red Sox': null, + 'Detroit Tigers': null, + 'New York Yankees': null + } +}; diff --git a/encoding/yaml/__test__/samples-common/construct-set.yml b/encoding/yaml/__test__/samples-common/construct-set.yml new file mode 100644 index 000000000000..e05dc885796e --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-set.yml @@ -0,0 +1,7 @@ +# 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 } diff --git a/encoding/yaml/__test__/samples-common/construct-str-ascii.js b/encoding/yaml/__test__/samples-common/construct-str-ascii.js new file mode 100644 index 000000000000..9b5197a2744d --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-str-ascii.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = 'ascii string'; diff --git a/encoding/yaml/__test__/samples-common/construct-str-ascii.yml b/encoding/yaml/__test__/samples-common/construct-str-ascii.yml new file mode 100644 index 000000000000..0d93013b5d9b --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-str-ascii.yml @@ -0,0 +1 @@ +--- !!str "ascii string" diff --git a/encoding/yaml/__test__/samples-common/construct-str-utf8.js b/encoding/yaml/__test__/samples-common/construct-str-utf8.js new file mode 100644 index 000000000000..1bb7e4aa062d --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-str-utf8.js @@ -0,0 +1,7 @@ +// 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. + +/*eslint-disable max-len*/ + +module.exports = '\u042d\u0442\u043e \u0443\u043d\u0438\u043a\u043e\u0434\u043d\u0430\u044f \u0441\u0442\u0440\u043e\u043a\u0430'; diff --git a/encoding/yaml/__test__/samples-common/construct-str-utf8.yml b/encoding/yaml/__test__/samples-common/construct-str-utf8.yml new file mode 100644 index 000000000000..e355f184a64c --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-str-utf8.yml @@ -0,0 +1 @@ +--- !!str "Это уникодная строка" diff --git a/encoding/yaml/__test__/samples-common/construct-str.js b/encoding/yaml/__test__/samples-common/construct-str.js new file mode 100644 index 000000000000..6e37bae46f37 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-str.js @@ -0,0 +1,7 @@ +// 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. + +module.exports = { + string: 'abcd' +}; diff --git a/encoding/yaml/__test__/samples-common/construct-str.yml b/encoding/yaml/__test__/samples-common/construct-str.yml new file mode 100644 index 000000000000..606ac6b290dc --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-str.yml @@ -0,0 +1 @@ +string: abcd diff --git a/encoding/yaml/__test__/samples-common/construct-string-types.js b/encoding/yaml/__test__/samples-common/construct-string-types.js new file mode 100644 index 000000000000..be1a4f0451af --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-string-types.js @@ -0,0 +1,116 @@ +// 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. + +var essay = 'a\n' + + 'b\n' + + '1sdf 2ar 3sdf 4sdf 5sdf 6sdf 7sdf 8sdf 9sdf 10asdf 11asdf ' + + '12asdf 13asdf 14asdf 15df 16df long 17df 1890 1900 2000 ' + + '2001 2002 baz\n' + + '2003 2004 4sdf 4sdf 7sdf 8sdf 9sdf 0sdf 1sdf 2sdf 3sdf ' + + '4sdf 5sdf 6sdf long 7sdf 8sdf 9sdf 0sdf 2002 asdfasdf ' + + '2003 2006 2020 2021 2022 2023 2024 2025 long 2026 2027 ' + + '2028 2029 2030 2031 2032 2033 2034 2035 asdfasdf 02036 ' + + '2040 2041 long 2042 2043 2044 2045 2046 2027 2048 ' + + '2050 4444 5555 6666 7777 8888 9999 long asdfasdf ' + + 'aaaa bbbb cccc dddd eeeee fff ggggggg hhhi iiii jjjj ' + + 'long asdfasdfasdfasdfslong ' + + ' xlong ' + + 'asdfasdfasdfasdfasdfasdfasdfasd asdf xasdf the end'; + +module.exports = { + simpleString: 'hello world', + simpleStringComma: 'hello, world', + stackTrace: 'Error: foo\n' + + ' at repl:1:5\n' + + ' at REPLServer.defaultEval (repl.js:116:27)\n' + + ' at bound (domain.js:254:14)', + + trailingSpace: 'hello space ', + trailingTab: 'hello tab \t', + trailingCR: 'hello newline\n', + + simpleQuotes: 'won\'t you be my neighbor', + unprintable: 'number 1 is \u0001 and the biggest byte is \uffff ok', + + multiline: 'hello\nworld\n', + multilineChomp: 'hello\nworld', + multilineTrailingCR: 'hello\nworld\n\n\n\n\n', + multilineTrailingSpace: 'hello\nworld \nspace\n', + multilineTrailingSpaceChomp: 'hello\nworld \nspace', + + longMultiBigSpace: 'x' + new Array(100).join(' ') + 'y\nworld\n', + longMultiBigSpaceChomp: 'x' + new Array(100).join(' ') + 'y\nworld', + + essayNoTrailing: essay, + essayManyTrailing: essay + '\n\n\n\n\n\n', + essayOneTrailing: essay + '\n', + + neggy: '-1', + questy: '?asdf', + + // Example 8.1. + blockScalarHeader: [ 'literal\n', ' folded\n', 'keep\n\n', ' strip' ], + // Example 8.2. + // The ' \t' is a more-indented line as per [177] s-nb-spaced-text. + blockIndentationIndicator: [ + 'detected\n', '\n\n# detected\n', ' explicit\n', '\t\ndetected\n' + ], + // Example 8.6. Empty Scalar Chomping + strip: '', + clip: '', + keep: '\n', + // Example 8.10. + foldedStyle: '\nfolded line\nnext line\n' + + ' * bullet\n\n * list\n * lines\n\nlast line\n', + + longMultiChomp: new Array(80).join('lo hel') + '\nworld', + longMultiTrailingCR: new Array(80).join('lo hel') + '\nworld\n\n\n\n\n', + longMulti: new Array(80).join('lo hel') + '\nworld\n' + +}; + +// now indent the long multi really far +var obj = module.exports, + i; + +for (i = 0; i < 5; i++) { + obj.indent = {}; + obj = obj.indent; +} + +obj.ind = module.exports.longMulti; + +for (i = 0; i < 5; i++) { + obj.indent = {}; + obj = obj.indent; +} + +obj.ind = module.exports.longMulti; + +for (i = 0; i < 5; i++) { + obj.indent = {}; + obj = obj.indent; +} + +obj.ind = module.exports.longMulti; + +for (i = 0; i < 5; i++) { + obj.indent = {}; + obj = obj.indent; +} + +obj.ind = module.exports.longMulti; + +for (i = 0; i < 5; i++) { + obj.indent = {}; + obj = obj.indent; +} + +obj.ind = module.exports.longMulti; + +for (i = 0; i < 5; i++) { + obj.indent = {}; + obj = obj.indent; +} +obj.ind = module.exports.longMulti; diff --git a/encoding/yaml/__test__/samples-common/construct-string-types.yml b/encoding/yaml/__test__/samples-common/construct-string-types.yml new file mode 100644 index 000000000000..e77f98618d06 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-string-types.yml @@ -0,0 +1,277 @@ +simpleString: hello world +simpleStringComma: 'hello, world' +stackTrace: |- + Error: foo + at repl:1:5 + at REPLServer.defaultEval (repl.js:116:27) + at bound (domain.js:254:14) +trailingSpace: 'hello space ' +trailingTab: "hello tab \t" +trailingCR: | + hello newline +simpleQuotes: |- + won't you be my neighbor +unprintable: "number 1 is \x01 and the biggest byte is \uFFFF ok" +multiline: | + hello + world +multilineChomp: |- + hello + world +multilineTrailingCR: |+ + hello + world + + + + +multilineTrailingSpace: "hello\nworld \nspace\n" +multilineTrailingSpaceChomp: "hello\nworld \nspace" +longMultiBigSpace: > + x y + + world +longMultiBigSpaceChomp: >- + x y + + world +essayNoTrailing: >- + a + + b + + 1sdf 2ar 3sdf 4sdf 5sdf 6sdf 7sdf 8sdf 9sdf 10asdf 11asdf 12asdf 13asdf 14asdf + 15df 16df long 17df 1890 1900 2000 2001 2002 baz + + 2003 2004 4sdf 4sdf 7sdf 8sdf 9sdf 0sdf 1sdf 2sdf 3sdf 4sdf 5sdf 6sdf + long 7sdf 8sdf 9sdf 0sdf 2002 asdfasdf 2003 2006 2020 2021 2022 2023 2024 2025 + long 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 asdfasdf 02036 2040 + 2041 long 2042 2043 2044 2045 2046 2027 2048 2050 4444 5555 6666 7777 8888 + 9999 long asdfasdf aaaa bbbb cccc dddd eeeee fff ggggggg hhhi iiii jjjj + long asdfasdfasdfasdfslong xlong asdfasdfasdfasdfasdfasdfasdfasd + asdf xasdf the end +essayManyTrailing: >+ + a + + b + + 1sdf 2ar 3sdf 4sdf 5sdf 6sdf 7sdf 8sdf 9sdf 10asdf 11asdf 12asdf 13asdf 14asdf + 15df 16df long 17df 1890 1900 2000 2001 2002 baz + + 2003 2004 4sdf 4sdf 7sdf 8sdf 9sdf 0sdf 1sdf 2sdf 3sdf 4sdf 5sdf 6sdf + long 7sdf 8sdf 9sdf 0sdf 2002 asdfasdf 2003 2006 2020 2021 2022 2023 2024 2025 + long 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 asdfasdf 02036 2040 + 2041 long 2042 2043 2044 2045 2046 2027 2048 2050 4444 5555 6666 7777 8888 + 9999 long asdfasdf aaaa bbbb cccc dddd eeeee fff ggggggg hhhi iiii jjjj + long asdfasdfasdfasdfslong xlong asdfasdfasdfasdfasdfasdfasdfasd + asdf xasdf the end + + + + + +essayOneTrailing: > + a + + b + + 1sdf 2ar 3sdf 4sdf 5sdf 6sdf 7sdf 8sdf 9sdf 10asdf 11asdf 12asdf 13asdf 14asdf + 15df 16df long 17df 1890 1900 2000 2001 2002 baz + + 2003 2004 4sdf 4sdf 7sdf 8sdf 9sdf 0sdf 1sdf 2sdf 3sdf 4sdf 5sdf 6sdf + long 7sdf 8sdf 9sdf 0sdf 2002 asdfasdf 2003 2006 2020 2021 2022 2023 2024 2025 + long 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 asdfasdf 02036 2040 + 2041 long 2042 2043 2044 2045 2046 2027 2048 2050 4444 5555 6666 7777 8888 + 9999 long asdfasdf aaaa bbbb cccc dddd eeeee fff ggggggg hhhi iiii jjjj + long asdfasdfasdfasdfslong xlong asdfasdfasdfasdfasdfasdfasdfasd + asdf xasdf the end +neggy: '-1' +questy: '?asdf' +blockScalarHeader: +- | # Empty header + literal +- >1 # Indentation indicator + folded +- |+ # Chomping indicator + keep + +- >1- # Both indicators + strip +blockIndentationIndicator: +- | + detected +- > + + + # detected +- |1 + explicit +- > + + detected +strip: >- + +clip: > + +keep: |+ + +foldedStyle: > + + folded + line + + next + line + * bullet + + * list + * lines + + last + line + +# Comment +longMultiChomp: >- + lo hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hel + + world +longMultiTrailingCR: >+ + lo hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hel + + world + + + + +longMulti: > + lo hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello hello hello + hello hel + + world +indent: + indent: + indent: + indent: + indent: + ind: > + lo hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello hello + hello hel + + world + indent: + indent: + indent: + indent: + indent: + ind: > + lo hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello hello hel + + world + indent: + indent: + indent: + indent: + indent: + ind: > + lo hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hello + hello hello hello hello hello hello hello hel + + world + indent: + indent: + indent: + indent: + indent: + ind: > + lo hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hel + + world + indent: + indent: + indent: + indent: + indent: + ind: > + lo hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hel + + world + indent: + indent: + indent: + indent: + indent: + ind: > + lo hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hello hello hello hello hello + hello hello hel + + world diff --git a/encoding/yaml/__test__/samples-common/construct-timestamp.js b/encoding/yaml/__test__/samples-common/construct-timestamp.js new file mode 100644 index 000000000000..4590a309696c --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-timestamp.js @@ -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. + +module.exports = { + canonical: new Date(Date.UTC(2001, 11, 15, 2, 59, 43, 100)), + 'valid iso8601': new Date(Date.UTC(2001, 11, 15, 2, 59, 43, 100)), + 'space separated': new Date(Date.UTC(2001, 11, 15, 2, 59, 43, 100)), + 'no time zone (Z)': new Date(Date.UTC(2001, 11, 15, 2, 59, 43, 100)), + 'date (00:00:00Z)': new Date(Date.UTC(2002, 11, 14)), + 'not a date': '2002-1-1' +}; diff --git a/encoding/yaml/__test__/samples-common/construct-timestamp.yml b/encoding/yaml/__test__/samples-common/construct-timestamp.yml new file mode 100644 index 000000000000..7cd9ac6bd1ce --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-timestamp.yml @@ -0,0 +1,6 @@ +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 +not a date: 2002-1-1 diff --git a/encoding/yaml/__test__/samples-common/construct-value.js b/encoding/yaml/__test__/samples-common/construct-value.js new file mode 100644 index 000000000000..192a0086642d --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-value.js @@ -0,0 +1,18 @@ +// 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. + +module.exports = [ + { + 'link with': [ + 'library1.dll', + 'library2.dll' + ] + }, + { + 'link with': [ + { '=': 'library1.dll', version: 1.2 }, + { '=': 'library2.dll', version: 2.3 } + ] + } +]; diff --git a/encoding/yaml/__test__/samples-common/construct-value.yml b/encoding/yaml/__test__/samples-common/construct-value.yml new file mode 100644 index 000000000000..3eb79198889e --- /dev/null +++ b/encoding/yaml/__test__/samples-common/construct-value.yml @@ -0,0 +1,10 @@ +--- # Old schema +link with: + - library1.dll + - library2.dll +--- # New schema +link with: + - = : library1.dll + version: 1.2 + - = : library2.dll + version: 2.3 diff --git a/encoding/yaml/__test__/samples-common/dump-empty-collections.js b/encoding/yaml/__test__/samples-common/dump-empty-collections.js new file mode 100644 index 000000000000..5e36090086f7 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/dump-empty-collections.js @@ -0,0 +1,8 @@ +// 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. + +module.exports = { + emptyArray: [], + emptyObject: {} +}; diff --git a/encoding/yaml/__test__/samples-common/dump-empty-collections.yml b/encoding/yaml/__test__/samples-common/dump-empty-collections.yml new file mode 100644 index 000000000000..69575751898c --- /dev/null +++ b/encoding/yaml/__test__/samples-common/dump-empty-collections.yml @@ -0,0 +1,2 @@ +emptyArray: [] +emptyObject: {} diff --git a/encoding/yaml/__test__/samples-common/duplicate-mapping-key.js b/encoding/yaml/__test__/samples-common/duplicate-mapping-key.js new file mode 100644 index 000000000000..9902c59acea3 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/duplicate-mapping-key.js @@ -0,0 +1,10 @@ +// 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. + +module.exports = { + foo: { + baz: 'bat', + foo: 'duplicate key' + } +}; diff --git a/encoding/yaml/__test__/samples-common/duplicate-mapping-key.yml b/encoding/yaml/__test__/samples-common/duplicate-mapping-key.yml new file mode 100644 index 000000000000..e2b22b397f62 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/duplicate-mapping-key.yml @@ -0,0 +1,3 @@ +foo: + baz: bat + foo: duplicate key diff --git a/encoding/yaml/__test__/samples-common/duplicate-merge-key.js b/encoding/yaml/__test__/samples-common/duplicate-merge-key.js new file mode 100644 index 000000000000..0fb70b7e6e03 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/duplicate-merge-key.js @@ -0,0 +1,11 @@ +// 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. + +module.exports = { + x: 1, + y: 2, + foo: 'bar', + z: 3, + t: 4 +}; diff --git a/encoding/yaml/__test__/samples-common/duplicate-merge-key.yml b/encoding/yaml/__test__/samples-common/duplicate-merge-key.yml new file mode 100644 index 000000000000..cebc3a18c48f --- /dev/null +++ b/encoding/yaml/__test__/samples-common/duplicate-merge-key.yml @@ -0,0 +1,4 @@ +--- +<<: {x: 1, y: 2} +foo: bar +<<: {z: 3, t: 4} diff --git a/encoding/yaml/__test__/samples-common/emitting-unacceptable-unicode-character-bug.js b/encoding/yaml/__test__/samples-common/emitting-unacceptable-unicode-character-bug.js new file mode 100644 index 000000000000..ce802314d387 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/emitting-unacceptable-unicode-character-bug.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = '\udd00'; diff --git a/encoding/yaml/__test__/samples-common/emitting-unacceptable-unicode-character-bug.yml b/encoding/yaml/__test__/samples-common/emitting-unacceptable-unicode-character-bug.yml new file mode 100644 index 000000000000..2a5df00dfa5c --- /dev/null +++ b/encoding/yaml/__test__/samples-common/emitting-unacceptable-unicode-character-bug.yml @@ -0,0 +1 @@ +"\udd00" diff --git a/encoding/yaml/__test__/samples-common/invalid-single-quote-bug.js b/encoding/yaml/__test__/samples-common/invalid-single-quote-bug.js new file mode 100644 index 000000000000..e4fdddd80c45 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/invalid-single-quote-bug.js @@ -0,0 +1,8 @@ +// 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. + +module.exports = [ + "foo 'bar'", + "foo\n'bar'" +]; diff --git a/encoding/yaml/__test__/samples-common/invalid-single-quote-bug.yml b/encoding/yaml/__test__/samples-common/invalid-single-quote-bug.yml new file mode 100644 index 000000000000..76ef7ae30b0f --- /dev/null +++ b/encoding/yaml/__test__/samples-common/invalid-single-quote-bug.yml @@ -0,0 +1,2 @@ +- "foo 'bar'" +- "foo\n'bar'" diff --git a/encoding/yaml/__test__/samples-common/more-floats.js b/encoding/yaml/__test__/samples-common/more-floats.js new file mode 100644 index 000000000000..6f3c2e570f95 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/more-floats.js @@ -0,0 +1,31 @@ +// 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. + +var assert = require('assert'); + +var expected = [ + 0.0, + 1.0, + -1.0, + Number.POSITIVE_INFINITY, + Number.NEGATIVE_INFINITY, + NaN, + NaN +]; + +function testHandler(actual) { + assert.strictEqual(Object.prototype.toString.call(actual), '[object Array]'); + assert.strictEqual(actual.length, 7); + assert.strictEqual(actual[0], expected[0]); + assert.strictEqual(actual[1], expected[1]); + assert.strictEqual(actual[2], expected[2]); + assert.strictEqual(actual[3], expected[3]); + assert.strictEqual(actual[4], expected[4]); + assert(Number.isNaN(actual[5])); + assert(Number.isNaN(actual[6])); +} + +testHandler.expected = expected; + +module.exports = testHandler; diff --git a/encoding/yaml/__test__/samples-common/more-floats.yml b/encoding/yaml/__test__/samples-common/more-floats.yml new file mode 100644 index 000000000000..399eb177b6e6 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/more-floats.yml @@ -0,0 +1 @@ +[0.0, +1.0, -1.0, +.inf, -.inf, .nan, .nan] diff --git a/encoding/yaml/__test__/samples-common/negative-float-bug.js b/encoding/yaml/__test__/samples-common/negative-float-bug.js new file mode 100644 index 000000000000..b65ec48c0faa --- /dev/null +++ b/encoding/yaml/__test__/samples-common/negative-float-bug.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = -1.0; diff --git a/encoding/yaml/__test__/samples-common/negative-float-bug.yml b/encoding/yaml/__test__/samples-common/negative-float-bug.yml new file mode 100644 index 000000000000..18e16e38c5de --- /dev/null +++ b/encoding/yaml/__test__/samples-common/negative-float-bug.yml @@ -0,0 +1 @@ +-1.0 diff --git a/encoding/yaml/__test__/samples-common/single-dot-is-not-float-bug.js b/encoding/yaml/__test__/samples-common/single-dot-is-not-float-bug.js new file mode 100644 index 000000000000..0fd08d34b67e --- /dev/null +++ b/encoding/yaml/__test__/samples-common/single-dot-is-not-float-bug.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = '.'; diff --git a/encoding/yaml/__test__/samples-common/single-dot-is-not-float-bug.yml b/encoding/yaml/__test__/samples-common/single-dot-is-not-float-bug.yml new file mode 100644 index 000000000000..9c558e357c41 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/single-dot-is-not-float-bug.yml @@ -0,0 +1 @@ +. diff --git a/encoding/yaml/__test__/samples-common/timestamp-bugs.js b/encoding/yaml/__test__/samples-common/timestamp-bugs.js new file mode 100644 index 000000000000..8d378934c854 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/timestamp-bugs.js @@ -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. + +module.exports = [ + new Date(Date.UTC(2001, 11, 15, 3, 29, 43, 100)), + new Date(Date.UTC(2001, 11, 14, 16, 29, 43, 100)), + new Date(Date.UTC(2001, 11, 14, 21, 59, 43, 1)), + new Date(Date.UTC(2001, 11, 14, (21 - 1), 59, 43, 0)), + new Date(Date.UTC(2001, 11, 14, (21 + 1), (59 + 30), 43, 0)), + new Date(Date.UTC(2005, 6, 8, 17, 35, 4, 517)) +]; diff --git a/encoding/yaml/__test__/samples-common/timestamp-bugs.yml b/encoding/yaml/__test__/samples-common/timestamp-bugs.yml new file mode 100644 index 000000000000..721d290820ce --- /dev/null +++ b/encoding/yaml/__test__/samples-common/timestamp-bugs.yml @@ -0,0 +1,6 @@ +- 2001-12-14 21:59:43.10 -5:30 +- 2001-12-14 21:59:43.10 +5:30 +- 2001-12-14 21:59:43.00101 +- 2001-12-14 21:59:43+1 +- 2001-12-14 21:59:43-1:30 +- 2005-07-08 17:35:04.517600 diff --git a/encoding/yaml/__test__/samples-common/utf8-implicit.js b/encoding/yaml/__test__/samples-common/utf8-implicit.js new file mode 100644 index 000000000000..33f04ea54784 --- /dev/null +++ b/encoding/yaml/__test__/samples-common/utf8-implicit.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = 'implicit UTF-8'; diff --git a/encoding/yaml/__test__/samples-common/utf8-implicit.yml b/encoding/yaml/__test__/samples-common/utf8-implicit.yml new file mode 100644 index 000000000000..9d8081e9e89c --- /dev/null +++ b/encoding/yaml/__test__/samples-common/utf8-implicit.yml @@ -0,0 +1 @@ +--- implicit UTF-8 diff --git a/encoding/yaml/__test__/samples-load-errors/a-nasty-libyaml-bug.yml b/encoding/yaml/__test__/samples-load-errors/a-nasty-libyaml-bug.yml new file mode 100644 index 000000000000..f97d49f85edc --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/a-nasty-libyaml-bug.yml @@ -0,0 +1 @@ +[ [ \ No newline at end of file diff --git a/encoding/yaml/__test__/samples-load-errors/document-separator-in-quoted-scalar.yml b/encoding/yaml/__test__/samples-load-errors/document-separator-in-quoted-scalar.yml new file mode 100644 index 000000000000..9eeb0d6fdd7d --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/document-separator-in-quoted-scalar.yml @@ -0,0 +1,11 @@ +--- +"this --- is correct" +--- +"this +...is also +correct" +--- +"a quoted scalar +cannot contain +--- +document separators" diff --git a/encoding/yaml/__test__/samples-load-errors/duplicate-key.js b/encoding/yaml/__test__/samples-load-errors/duplicate-key.js new file mode 100644 index 000000000000..3fa97b044082 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/duplicate-key.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = { foo: 'baz' }; diff --git a/encoding/yaml/__test__/samples-load-errors/duplicate-key.yml b/encoding/yaml/__test__/samples-load-errors/duplicate-key.yml new file mode 100644 index 000000000000..84deb8f20a82 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/duplicate-key.yml @@ -0,0 +1,3 @@ +--- +foo: bar +foo: baz diff --git a/encoding/yaml/__test__/samples-load-errors/duplicate-tag-directive.yml b/encoding/yaml/__test__/samples-load-errors/duplicate-tag-directive.yml new file mode 100644 index 000000000000..50c81a06a017 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/duplicate-tag-directive.yml @@ -0,0 +1,3 @@ +%TAG !foo! bar +%TAG !foo! baz +--- foo diff --git a/encoding/yaml/__test__/samples-load-errors/duplicate-value-key.js b/encoding/yaml/__test__/samples-load-errors/duplicate-value-key.js new file mode 100644 index 000000000000..0801e502962a --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/duplicate-value-key.js @@ -0,0 +1,5 @@ +// 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. + +module.exports = { foo: 'bar', '=': 2 }; diff --git a/encoding/yaml/__test__/samples-load-errors/duplicate-value-key.yml b/encoding/yaml/__test__/samples-load-errors/duplicate-value-key.yml new file mode 100644 index 000000000000..b34a1d6988a1 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/duplicate-value-key.yml @@ -0,0 +1,4 @@ +--- +=: 1 +foo: bar +=: 2 diff --git a/encoding/yaml/__test__/samples-load-errors/duplicate-yaml-directive.yml b/encoding/yaml/__test__/samples-load-errors/duplicate-yaml-directive.yml new file mode 100644 index 000000000000..9b723905f3bc --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/duplicate-yaml-directive.yml @@ -0,0 +1,3 @@ +%YAML 1.1 +%YAML 1.1 +--- foo diff --git a/encoding/yaml/__test__/samples-load-errors/expected-mapping.yml b/encoding/yaml/__test__/samples-load-errors/expected-mapping.yml new file mode 100644 index 000000000000..82aed98aa4a2 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/expected-mapping.yml @@ -0,0 +1 @@ +--- !!map [not, a, map] diff --git a/encoding/yaml/__test__/samples-load-errors/expected-scalar.yml b/encoding/yaml/__test__/samples-load-errors/expected-scalar.yml new file mode 100644 index 000000000000..7b3171e88bef --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/expected-scalar.yml @@ -0,0 +1 @@ +--- !!str [not a scalar] diff --git a/encoding/yaml/__test__/samples-load-errors/expected-sequence.yml b/encoding/yaml/__test__/samples-load-errors/expected-sequence.yml new file mode 100644 index 000000000000..08074ea52669 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/expected-sequence.yml @@ -0,0 +1 @@ +--- !!seq {foo, bar, baz} diff --git a/encoding/yaml/__test__/samples-load-errors/fetch-complex-value-bug.yml b/encoding/yaml/__test__/samples-load-errors/fetch-complex-value-bug.yml new file mode 100644 index 000000000000..25fac24e62b9 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/fetch-complex-value-bug.yml @@ -0,0 +1,2 @@ +? "foo" + : "bar" diff --git a/encoding/yaml/__test__/samples-load-errors/forbidden-entry.yml b/encoding/yaml/__test__/samples-load-errors/forbidden-entry.yml new file mode 100644 index 000000000000..f2e307962480 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/forbidden-entry.yml @@ -0,0 +1,2 @@ +test: - foo + - bar diff --git a/encoding/yaml/__test__/samples-load-errors/forbidden-key.yml b/encoding/yaml/__test__/samples-load-errors/forbidden-key.yml new file mode 100644 index 000000000000..da9b471ddfaa --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/forbidden-key.yml @@ -0,0 +1,2 @@ +test: ? foo + : bar diff --git a/encoding/yaml/__test__/samples-load-errors/forbidden-value.yml b/encoding/yaml/__test__/samples-load-errors/forbidden-value.yml new file mode 100644 index 000000000000..efd7ce583ddf --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/forbidden-value.yml @@ -0,0 +1 @@ +test: key: value diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-anchor-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-anchor-2.yml new file mode 100644 index 000000000000..bfc4ff01f386 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-anchor-2.yml @@ -0,0 +1,8 @@ +--- +- [ + &correct foo, + *correct, + *correct] # still correct +- *correct: still correct +- &correct-or-not[foo, bar] + diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-base64-data-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-base64-data-2.yml new file mode 100644 index 000000000000..2553a4f329b6 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-base64-data-2.yml @@ -0,0 +1,2 @@ +--- !!binary + двоичные данные в base64 diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-base64-data.yml b/encoding/yaml/__test__/samples-load-errors/invalid-base64-data.yml new file mode 100644 index 000000000000..798abbae9afd --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-base64-data.yml @@ -0,0 +1,2 @@ +--- !!binary + binary data encoded in base64 should be here. diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-block-scalar-indicator.yml b/encoding/yaml/__test__/samples-load-errors/invalid-block-scalar-indicator.yml new file mode 100644 index 000000000000..16a6db1833cb --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-block-scalar-indicator.yml @@ -0,0 +1,2 @@ +--- > what is this? # a comment +data diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-character.yml b/encoding/yaml/__test__/samples-load-errors/invalid-character.yml new file mode 100644 index 000000000000..03687b02d41c Binary files /dev/null and b/encoding/yaml/__test__/samples-load-errors/invalid-character.yml differ diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-directive-line.yml b/encoding/yaml/__test__/samples-load-errors/invalid-directive-line.yml new file mode 100644 index 000000000000..0892eb66dde8 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-directive-line.yml @@ -0,0 +1,2 @@ +%YAML 1.1 ? # extra symbol +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-directive-name-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-directive-name-1.yml new file mode 100644 index 000000000000..153fd889aedf --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-directive-name-1.yml @@ -0,0 +1,2 @@ +% # no name at all +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-directive-name-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-directive-name-2.yml new file mode 100644 index 000000000000..3732a06a34aa --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-directive-name-2.yml @@ -0,0 +1,2 @@ +%invalid-characters:in-directive name +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-escape-character.yml b/encoding/yaml/__test__/samples-load-errors/invalid-escape-character.yml new file mode 100644 index 000000000000..a95ab7678642 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-escape-character.yml @@ -0,0 +1 @@ +"some escape characters are \ncorrect, but this one \?\nis not\n" diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-escape-numbers.yml b/encoding/yaml/__test__/samples-load-errors/invalid-escape-numbers.yml new file mode 100644 index 000000000000..614ec9f5ad7e --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-escape-numbers.yml @@ -0,0 +1 @@ +"hm.... \u123?" diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-indentation-indicator-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-indentation-indicator-1.yml new file mode 100644 index 000000000000..a3cd12f5fe5a --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-indentation-indicator-1.yml @@ -0,0 +1,2 @@ +--- >0 # not valid +data diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-indentation-indicator-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-indentation-indicator-2.yml new file mode 100644 index 000000000000..eefb6ecd2950 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-indentation-indicator-2.yml @@ -0,0 +1,2 @@ +--- >-0 +data diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-item-without-trailing-break.yml b/encoding/yaml/__test__/samples-load-errors/invalid-item-without-trailing-break.yml new file mode 100644 index 000000000000..fdcf6c6b77a3 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-item-without-trailing-break.yml @@ -0,0 +1,2 @@ +- +-0 \ No newline at end of file diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-merge-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-merge-1.yml new file mode 100644 index 000000000000..fc3c2844d9b4 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-merge-1.yml @@ -0,0 +1,2 @@ +foo: bar +<<: baz diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-merge-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-merge-2.yml new file mode 100644 index 000000000000..8e88615c5945 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-merge-2.yml @@ -0,0 +1,2 @@ +foo: bar +<<: [x: 1, y: 2, z, t: 4] diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-omap-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-omap-1.yml new file mode 100644 index 000000000000..28633926e16f --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-omap-1.yml @@ -0,0 +1,3 @@ +--- !!omap +foo: bar +baz: bat diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-omap-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-omap-2.yml new file mode 100644 index 000000000000..c377dfb81ab9 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-omap-2.yml @@ -0,0 +1,3 @@ +--- !!omap +- foo: bar +- baz diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-omap-3.yml b/encoding/yaml/__test__/samples-load-errors/invalid-omap-3.yml new file mode 100644 index 000000000000..2a4f50d9846a --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-omap-3.yml @@ -0,0 +1,4 @@ +--- !!omap +- foo: bar +- baz: bar + bar: bar diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-pairs-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-pairs-1.yml new file mode 100644 index 000000000000..42d19aecfcc9 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-pairs-1.yml @@ -0,0 +1,3 @@ +--- !!pairs +foo: bar +baz: bat diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-pairs-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-pairs-2.yml new file mode 100644 index 000000000000..31389eaeb247 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-pairs-2.yml @@ -0,0 +1,3 @@ +--- !!pairs +- foo: bar +- baz diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-pairs-3.yml b/encoding/yaml/__test__/samples-load-errors/invalid-pairs-3.yml new file mode 100644 index 000000000000..f8d7704edff6 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-pairs-3.yml @@ -0,0 +1,4 @@ +--- !!pairs +- foo: bar +- baz: bar + bar: bar diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-simple-key.yml b/encoding/yaml/__test__/samples-load-errors/invalid-simple-key.yml new file mode 100644 index 000000000000..a58deecf9762 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-simple-key.yml @@ -0,0 +1,3 @@ +key: value +invalid simple key +next key: next value diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-starting-character.yml b/encoding/yaml/__test__/samples-load-errors/invalid-starting-character.yml new file mode 100644 index 000000000000..bb81c60ce9ea --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-starting-character.yml @@ -0,0 +1 @@ +@@@@@@@@@@@@@@@@@@@ diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-tag-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-tag-2.yml new file mode 100644 index 000000000000..3a36700a450d --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-tag-2.yml @@ -0,0 +1 @@ +- !prefix!foo#bar baz diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-tag-directive-handle.yml b/encoding/yaml/__test__/samples-load-errors/invalid-tag-directive-handle.yml new file mode 100644 index 000000000000..42b5d7e99ec6 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-tag-directive-handle.yml @@ -0,0 +1,2 @@ +%TAG !!! !!! +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-tag-handle-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-tag-handle-1.yml new file mode 100644 index 000000000000..ef0d1430f46b --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-tag-handle-1.yml @@ -0,0 +1,2 @@ +%TAG foo bar +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-tag-handle-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-tag-handle-2.yml new file mode 100644 index 000000000000..06c7f0e47b48 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-tag-handle-2.yml @@ -0,0 +1,2 @@ +%TAG !foo bar +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-uri-escapes-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-uri-escapes-1.yml new file mode 100644 index 000000000000..a6ecb36a6c0a --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-uri-escapes-1.yml @@ -0,0 +1 @@ +--- ! foo diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-uri.yml b/encoding/yaml/__test__/samples-load-errors/invalid-uri.yml new file mode 100644 index 000000000000..06307e06a1d3 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-uri.yml @@ -0,0 +1 @@ +--- !foo! bar diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-1.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-1.yml new file mode 100644 index 000000000000..e9b4e3a67f78 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-1.yml @@ -0,0 +1,3 @@ +# No version at all. +%YAML +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-2.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-2.yml new file mode 100644 index 000000000000..6aa7740e97a2 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-2.yml @@ -0,0 +1,2 @@ +%YAML 1e-5 +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-3.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-3.yml new file mode 100644 index 000000000000..345e7842d4a3 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-3.yml @@ -0,0 +1,2 @@ +%YAML 1. +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-4.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-4.yml new file mode 100644 index 000000000000..b35ca820b0d2 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-4.yml @@ -0,0 +1,2 @@ +%YAML 1.132.435 +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-5.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-5.yml new file mode 100644 index 000000000000..7c2b49f5f09c --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-5.yml @@ -0,0 +1,2 @@ +%YAML A.0 +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-6.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-6.yml new file mode 100644 index 000000000000..bae714fcea6e --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-directive-version-6.yml @@ -0,0 +1,2 @@ +%YAML 123.C +--- diff --git a/encoding/yaml/__test__/samples-load-errors/invalid-yaml-version.yml b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-version.yml new file mode 100644 index 000000000000..dd019488b8c8 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/invalid-yaml-version.yml @@ -0,0 +1,2 @@ +%YAML 2.0 +--- foo diff --git a/encoding/yaml/__test__/samples-load-errors/no-block-collection-end.yml b/encoding/yaml/__test__/samples-load-errors/no-block-collection-end.yml new file mode 100644 index 000000000000..02d4d377832d --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-block-collection-end.yml @@ -0,0 +1,3 @@ +- foo +- bar +baz: bar diff --git a/encoding/yaml/__test__/samples-load-errors/no-block-mapping-end-2.yml b/encoding/yaml/__test__/samples-load-errors/no-block-mapping-end-2.yml new file mode 100644 index 000000000000..be63571f5127 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-block-mapping-end-2.yml @@ -0,0 +1,3 @@ +? foo +: bar +: baz diff --git a/encoding/yaml/__test__/samples-load-errors/no-block-mapping-end.yml b/encoding/yaml/__test__/samples-load-errors/no-block-mapping-end.yml new file mode 100644 index 000000000000..1ea921cf8d07 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-block-mapping-end.yml @@ -0,0 +1 @@ +foo: "bar" "baz" diff --git a/encoding/yaml/__test__/samples-load-errors/no-document-start.yml b/encoding/yaml/__test__/samples-load-errors/no-document-start.yml new file mode 100644 index 000000000000..c725ec8b6b53 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-document-start.yml @@ -0,0 +1,3 @@ +%YAML 1.1 +# no --- +foo: bar diff --git a/encoding/yaml/__test__/samples-load-errors/no-flow-mapping-end.yml b/encoding/yaml/__test__/samples-load-errors/no-flow-mapping-end.yml new file mode 100644 index 000000000000..8bd1403fdcd2 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-flow-mapping-end.yml @@ -0,0 +1 @@ +{ foo: bar ] diff --git a/encoding/yaml/__test__/samples-load-errors/no-flow-sequence-end.yml b/encoding/yaml/__test__/samples-load-errors/no-flow-sequence-end.yml new file mode 100644 index 000000000000..750d973b42c1 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-flow-sequence-end.yml @@ -0,0 +1 @@ +[foo, bar} diff --git a/encoding/yaml/__test__/samples-load-errors/no-node-1.yml b/encoding/yaml/__test__/samples-load-errors/no-node-1.yml new file mode 100644 index 000000000000..07b15009842b --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-node-1.yml @@ -0,0 +1 @@ +- !foo ] diff --git a/encoding/yaml/__test__/samples-load-errors/no-node-2.yml b/encoding/yaml/__test__/samples-load-errors/no-node-2.yml new file mode 100644 index 000000000000..563e3b341bae --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/no-node-2.yml @@ -0,0 +1 @@ +- [ !foo } ] diff --git a/encoding/yaml/__test__/samples-load-errors/remove-possible-simple-key-bug.yml b/encoding/yaml/__test__/samples-load-errors/remove-possible-simple-key-bug.yml new file mode 100644 index 000000000000..fe1bc6c502aa --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/remove-possible-simple-key-bug.yml @@ -0,0 +1,3 @@ +foo: &A bar +*A ] # The ']' indicator triggers remove_possible_simple_key, + # which should raise an error. diff --git a/encoding/yaml/__test__/samples-load-errors/unclosed-bracket.yml b/encoding/yaml/__test__/samples-load-errors/unclosed-bracket.yml new file mode 100644 index 000000000000..8c820777f6b8 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/unclosed-bracket.yml @@ -0,0 +1,6 @@ +test: + - [ foo: bar +# comment the rest of the stream to let the scanner detect the problem. +# - baz +#"we could have detected the unclosed bracket on the above line, but this would forbid such syntax as": { +#} diff --git a/encoding/yaml/__test__/samples-load-errors/unclosed-quoted-scalar.yml b/encoding/yaml/__test__/samples-load-errors/unclosed-quoted-scalar.yml new file mode 100644 index 000000000000..85374294d11b --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/unclosed-quoted-scalar.yml @@ -0,0 +1,2 @@ +'foo + bar diff --git a/encoding/yaml/__test__/samples-load-errors/undefined-anchor.yml b/encoding/yaml/__test__/samples-load-errors/undefined-anchor.yml new file mode 100644 index 000000000000..94691032f949 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/undefined-anchor.yml @@ -0,0 +1,3 @@ +- foo +- &bar baz +- *bat diff --git a/encoding/yaml/__test__/samples-load-errors/undefined-tag-handle.yml b/encoding/yaml/__test__/samples-load-errors/undefined-tag-handle.yml new file mode 100644 index 000000000000..82ba335c6d71 --- /dev/null +++ b/encoding/yaml/__test__/samples-load-errors/undefined-tag-handle.yml @@ -0,0 +1 @@ +--- !foo!bar baz diff --git a/encoding/yaml/__test__/support/schema.js b/encoding/yaml/__test__/support/schema.js new file mode 100644 index 000000000000..0c63b15feebc --- /dev/null +++ b/encoding/yaml/__test__/support/schema.js @@ -0,0 +1,115 @@ +// 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. + +var util = require('util'); +var yaml = require('../../lib/js-yaml'); + + +function Tag1(parameters) { + this.x = parameters.x; + this.y = parameters.y || 0; + this.z = parameters.z || 0; +} + + +function Tag2() { + Tag1.apply(this, arguments); +} +util.inherits(Tag2, Tag1); + + +function Tag3() { + Tag2.apply(this, arguments); +} +util.inherits(Tag3, Tag2); + + +function Foo(parameters) { + this.myParameter = parameters.myParameter; + this.myAnotherParameter = parameters.myAnotherParameter; +} + + +var TEST_SCHEMA = yaml.Schema.create([ + // NOTE: Type order matters! + // Inherited classes must precede their parents because the dumper + // doesn't inspect class inheritance and just picks first suitable + // class from this array. + new yaml.Type('!tag3', { + kind: 'mapping', + resolve: function (data) { + if (data === null) return false; + if (!Object.prototype.hasOwnProperty.call(data, '=') && + !Object.prototype.hasOwnProperty.call(data, 'x')) { + return false; + } + if (!Object.keys(data).every(function (k) { return k === '=' || k === 'x' || k === 'y' || k === 'z'; })) { + return false; + } + return true; + }, + construct: function (data) { + return new Tag3({ x: (data['='] || data.x), y: data.y, z: data.z }); + }, + instanceOf: Tag3, + represent: function (object) { + return { '=': object.x, y: object.y, z: object.z }; + } + }), + new yaml.Type('!tag2', { + kind: 'scalar', + construct: function (data) { + return new Tag2({ x: (typeof data === 'number') ? data : parseInt(data, 10) }); + }, + instanceOf: Tag2, + represent: function (object) { + return String(object.x); + } + }), + new yaml.Type('!tag1', { + kind: 'mapping', + resolve: function (data) { + if (data === null) return false; + if (!Object.prototype.hasOwnProperty.call(data, 'x')) return false; + if (!Object.keys(data).every(function (k) { return k === 'x' || k === 'y' || k === 'z'; })) { + return false; + } + return true; + }, + construct: function (data) { + return new Tag1({ x: data.x, y: data.y, z: data.z }); + }, + instanceOf: Tag1 + }), + new yaml.Type('!foo', { + kind: 'mapping', + resolve: function (data) { + if (data === null) return false; + if (!Object.keys(data).every(function (k) { return k === 'my-parameter' || k === 'my-another-parameter'; })) { + return false; + } + return true; + }, + construct: function (data) { + return new Foo({ + myParameter: data['my-parameter'], + myAnotherParameter: data['my-another-parameter'] + }); + }, + instanceOf: Foo, + represent: function (object) { + return { + 'my-parameter': object.myParameter, + 'my-another-parameter': object.myAnotherParameter + }; + } + }) +]); + + +module.exports.Tag1 = Tag1; +module.exports.Tag2 = Tag2; +module.exports.Tag3 = Tag3; +module.exports.Foo = Foo; +module.exports.TEST_SCHEMA = TEST_SCHEMA; diff --git a/encoding/yaml/__test__/units/alias-nodes.js b/encoding/yaml/__test__/units/alias-nodes.js new file mode 100644 index 000000000000..ea8069053fb7 --- /dev/null +++ b/encoding/yaml/__test__/units/alias-nodes.js @@ -0,0 +1,66 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../lib/js-yaml'); + + +function TestClass(data) { + var self = this; + Object.keys(data).forEach(function (key) { self[key] = data[key]; }); +} + +var TestClassYaml = new yaml.Type('!test', { + kind: 'mapping', + construct: function (data) { return new TestClass(data); } +}); + +var TEST_SCHEMA = yaml.Schema.create([ TestClassYaml ]); + + +suite('Alias nodes', function () { + suite('Resolving of an alias node should result the resolved and contructed value of the anchored node', function () { + test('Simple built-in primitives', function () { + assert.strictEqual(yaml.load('[&1 "foobar", *1]')[1], 'foobar'); + assert.strictEqual(yaml.load('[&1 ~, *1]')[1], null); + assert.strictEqual(yaml.load('[&1 true, *1]')[1], true); + assert.strictEqual(yaml.load('[&1 42, *1]')[1], 42); + }); + + test('Simple built-in objects', function () { + assert.deepEqual(yaml.load('[&1 [a, b, c, d], *1]')[1], [ 'a', 'b', 'c', 'd' ]); + assert.deepEqual(yaml.load('[&1 {a: b, c: d}, *1]')[1], { a: 'b', c: 'd' }); + }); + + test('Recursive built-in objects', function () { + var actual = yaml.load('[&1 {self: *1}, *1]')[1]; + + assert(actual === actual.self); + }); + + test("JavaScript-specific objects (JS-YAML's own extension)", function () { + var actual = yaml.load('[&1 !!js/function "function sum(a, b) { return a + b }", *1]')[1]; + + assert.strictEqual(Object.prototype.toString.call(actual), '[object Function]'); + assert.strictEqual(actual(10, 5), 15); + }); + + test('Simple custom objects', function () { + var expected = new TestClass({ a: 'b', c: 'd' }), + actual = yaml.load('[&1 !test {a: b, c: d}, *1]', { schema: TEST_SCHEMA })[1]; + + assert(actual instanceof TestClass); + assert.deepEqual(actual, expected); + }); + + // TODO: Not implemented yet (see issue #141) + test.skip('Recursive custom objects', function () { + var actual = yaml.load('[&1 !test {self: *1}, *1]', { schema: TEST_SCHEMA })[1]; + + assert(actual instanceof TestClass); + assert(actual.self instanceof TestClass); + assert(actual === actual.self); + }); + }); +}); diff --git a/encoding/yaml/__test__/units/bom-strip.js b/encoding/yaml/__test__/units/bom-strip.js new file mode 100644 index 000000000000..169abca0c7df --- /dev/null +++ b/encoding/yaml/__test__/units/bom-strip.js @@ -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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('BOM strip', function () { + assert.deepEqual(yaml.safeLoad('\uFEFFfoo: bar\n'), { foo: 'bar' }); + assert.deepEqual(yaml.safeLoad('foo: bar\n'), { foo: 'bar' }); +}); diff --git a/encoding/yaml/__test__/units/character-set.js b/encoding/yaml/__test__/units/character-set.js new file mode 100644 index 000000000000..a94ea3183d60 --- /dev/null +++ b/encoding/yaml/__test__/units/character-set.js @@ -0,0 +1,29 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Allow astral characters', function () { + assert.deepEqual(yaml.load('𝑘𝑒𝑦: 𝑣𝑎𝑙𝑢𝑒'), { '𝑘𝑒𝑦': '𝑣𝑎𝑙𝑢𝑒' }); +}); + +test('Forbid non-printable characters', function () { + assert.throws(function () { yaml.load('\x01'); }, yaml.YAMLException); + assert.throws(function () { yaml.load('\x7f'); }, yaml.YAMLException); + assert.throws(function () { yaml.load('\x9f'); }, yaml.YAMLException); +}); + +test('Forbid lone surrogates', function () { + assert.throws(function () { yaml.load('\udc00\ud800'); }, yaml.YAMLException); +}); + +test('Allow non-printable characters inside quoted scalars', function () { + assert.strictEqual(yaml.load('"\x7f\x9f\udc00\ud800"'), '\x7f\x9f\udc00\ud800'); +}); + +test('Forbid control sequences inside quoted scalars', function () { + assert.throws(function () { yaml.load('"\x03"'); }, yaml.YAMLException); +}); diff --git a/encoding/yaml/__test__/units/dump-scalar-styles.js b/encoding/yaml/__test__/units/dump-scalar-styles.js new file mode 100644 index 000000000000..fdd7cd1872ef --- /dev/null +++ b/encoding/yaml/__test__/units/dump-scalar-styles.js @@ -0,0 +1,247 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../..'); + +// Indents lines by 2 spaces. Empty lines (\n only) are not indented. +function indent(string) { + return string.replace(/^.+/gm, ' ' + '$&'); +} + +function getLength(s) { + return s.length; +} + +// Repeats a string n times. +function repeat(string, n) { + return (new Array(n + 1)).join(string); +} + +suite('Scalar style dump:', function () { + + suite('Plain style', function () { + test('is preferred', function () { + [ 'plain', + 'hello world', + 'pizza 3.14159', + // cannot be misinterpreted as a number + '127.0.0.1', + // quotes are allowed after the first character + 'quoted"scalar', + 'here\'s to "quotes"', + // additional allowed characters + '100% safe non-first characters? Of course!', + 'Jack & Jill ' + ].forEach(function (string) { + assert.strictEqual(yaml.safeDump(string), string + '\n'); + }); + }); + + test('disallows flow indicators inside flow collections', function () { + assert.strictEqual(yaml.safeDump({ quote: 'mispell [sic]' }, { flowLevel: 0 }), + "{quote: 'mispell [sic]'}\n"); + assert.strictEqual(yaml.safeDump({ key: 'no commas, either' }, { flowLevel: 0 }), + "{key: 'no commas, either'}\n"); + }); + }); + + suite('Single- and double-quoted styles', function () { + test('quote strings of ambiguous type', function () { + assert.strictEqual(yaml.safeDump('Yes'), '\'Yes\'\n'); + assert.strictEqual(yaml.safeDump('true'), '\'true\'\n'); + assert.strictEqual(yaml.safeDump('42'), '\'42\'\n'); + assert.strictEqual(yaml.safeDump('99.9'), '\'99.9\'\n'); + assert.strictEqual(yaml.safeDump('127.0001'), '\'127.0001\'\n'); + assert.strictEqual(yaml.safeDump('1.23015e+3'), '\'1.23015e+3\'\n'); + }); + + test('quote leading/trailing whitespace', function () { + assert.strictEqual(yaml.safeDump(' leading space'), '\' leading space\'\n'); + assert.strictEqual(yaml.safeDump('trailing space '), '\'trailing space \'\n'); + }); + + test('quote leading quotes', function () { + assert.strictEqual(yaml.safeDump("'singles double'"), "'''singles double'''\n"); + assert.strictEqual(yaml.safeDump('"single double'), '\'"single double\'\n'); + }); + + test('escape \\ and " in double-quoted', function () { + assert.strictEqual(yaml.safeDump('\u0007 escape\\ escaper"'), '"\\a escape\\\\ escaper\\""\n'); + }); + + test('escape non-printables', function () { + assert.strictEqual(yaml.safeDump('a\nb\u0001c'), '"a\\nb\\x01c"\n'); + }); + }); + + suite('Literal style', function () { + var content = 'a\nb \n\n c\n d', indented = indent(content); + + test('preserves trailing newlines using chomping', function () { + assert.strictEqual(yaml.safeDump({ a: '\n', b: '\n\n', c: 'c\n', d: 'd\nd' }), + 'a: |+\n\nb: |+\n\n\nc: |\n c\nd: |-\n d\n d\n'); + assert.strictEqual(yaml.safeDump('\n'), '|+\n' + '\n'); + assert.strictEqual(yaml.safeDump('\n\n'), '|+\n' + '\n\n'); + + assert.strictEqual(yaml.safeDump(content), '|-\n' + indented + '\n'); + assert.strictEqual(yaml.safeDump(content + '\n'), '|\n' + indented + '\n'); + assert.strictEqual(yaml.safeDump(content + '\n\n'), '|+\n' + indented + '\n\n'); + assert.strictEqual(yaml.safeDump(content + '\n\n\n'), '|+\n' + indented + '\n\n\n'); + }); + + test('accepts leading whitespace', function () { + assert.strictEqual(yaml.safeDump(' ' + content), '|2-\n ' + indented + '\n'); + }); + + test('falls back to quoting when required indent indicator is too large', function () { + assert.strictEqual(yaml.safeDump(' these go\nup to\neleven', { indent: 11 }), + '" these go\\nup to\\neleven"\n'); + }); + + test('does not use block style for multiline key', function () { + assert.strictEqual(yaml.safeDump({ + 'push\nand': { + you: 'pull' + } + }), '"push\\nand":\n you: pull\n'); + }); + }); + + suite('Folded style', function () { + (function () { + var content = (function () { + var result = ''; + var i = 1000; + for (var para = 1; para <= 7; para++) { + result += '\n'; + // indent paragraphs 3 and 4 + if (para === 3 || para === 4) { + result += repeat(' ', para); + } + // vary the number of words on the last line + for (var count = 2 * (30 / 5) + para - 1; count > 0; count--) { + result += i + ' '; + if (i % 17 === 0) result += ' '; + i++; + } + } + return result; + }()); + var wrapped = '\n' + + '1000 1001 1002 1003 1004 1005\n' + + '1006 1007 1008 1009 1010 1011 \n' + + '\n' + + '1012 1013 1014 1015 1016 1017\n' + + '1018 1019 1020 1021 1022 1023\n' + + '1024 \n' + + ' 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 \n' + + ' 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 \n' + + '1054 1055 1056 1057 1058 1059\n' + + '1060 1061 1062 1063 1064 1065\n' + + '1066 1067 1068 1069 \n' + + '\n' + + '1070 1071 1072 1073 1074 1075\n' + + '1076 1077 1078 1079 1080 1081\n' + + '1082 1083 1084 1085 1086 \n' + + '\n' + + '1087 1088 1089 1090 1091 1092\n' + + '1093 1094 1095 1096 1097 1098\n' + + '1099 1100 1101 1102 1103 1104 '; + var indented = indent(wrapped); + + function dumpNarrow(s) { + return yaml.safeDump(s, { lineWidth: 30 + 2 }); + } + + test('wraps lines and ignores more-indented lines ', function () { + assert.strictEqual(dumpNarrow(content), '>-\n' + indented + '\n'); + }); + + test('preserves trailing newlines using chomping', function () { + assert.strictEqual(dumpNarrow(content + '\n'), '>\n' + indented + '\n'); + assert.strictEqual(dumpNarrow(content + '\n\n'), '>+\n' + indented + '\n\n'); + assert.strictEqual(dumpNarrow(content + '\n\n\n'), '>+\n' + indented + '\n\n\n'); + }); + }()); + + // Dump and check that dump-then-load preserves content (is the identity function). + function dump(input, opts) { + var output = yaml.safeDump(input, opts); + assert.strictEqual(yaml.safeLoad(output), input, 'Dump then load should preserve content'); + return output; + } + + test('should not cut off a long word at the start of a line', function () { + assert.strictEqual(dump('123\n' + repeat('1234567890', 9) + ' hello\ngoodbye'), + '>-\n' + indent( + '123\n' + + '\n' + + repeat('1234567890', 9) + '\n' + + 'hello\n' + + '\n' + + 'goodbye\n')); + }); + + test('preserves consecutive spaces', function () { + var alphabet = 'a bc def ghi' + repeat(' ', 70) + 'jk lmn o\n' + + ' p qrstu v' + repeat(' ', 80) + '\nw x\n' + 'yz '; + assert.strictEqual(dump(alphabet), + '>-\n' + indent( + 'a bc def \n' + + 'ghi' + repeat(' ', 70) + 'jk \n' + + 'lmn o\n' + + ' p qrstu v' + repeat(' ', 80) + '\n' + + 'w x\n' + + '\n' + + 'yz \n')); + + var indeed = repeat('word. ', 31) + '\n' + + [ 2, 3, 5, 7, 11, 13, 17 ] + .map(function (n) { return repeat(' ', n); }) + .join('\n'); + assert.strictEqual(dump(indeed), + '>-\n' + indent( + 'word. word. word. word. word. word. word. word. word. word. word. word. word.\n' + + 'word. word. word. word. word. word. word. word. word. word. word. word. word.\n' + + 'word. word. word. word. word. \n' + + [ 2, 3, 5, 7, 11, 13, 17 ] + .map(function (n) { return repeat(' ', n); }) + .join('\n') + '\n')); + }); + + var story = 'Call me Ishmael. Some years ago—never mind how long precisely—' + + 'having little or no money in my purse, ' + + 'and nothing particular to interest me on shore, ' + + 'I thought I would sail about a little and see the watery part of the world...'; + var prefix = 'var short_story = "",'; + var line = 'longer_story = "' + story + '";'; + + test('should fold a long last line missing an ending newline', function () { + var content = [ prefix, line ].join('\n'); + + var lengths = dump(content).split('\n').map(getLength); + assert.deepEqual(lengths, [ 2, 23, 0, 69, 76, 80, 24, 0 ]); + }); + + test('should not fold a more-indented last line', function functionName() { + var content = [ prefix, line, ' ' + line ].join('\n'); + + var lengths = dump(content).split('\n').map(getLength); + assert.deepEqual(lengths, [ 2, 23, 0, 69, 76, 80, 24, 250, 0 ]); + }); + + test('should not fold when lineWidth === -1', function () { + var content = [ prefix, line, line + line, line ].join('\n'); + + assert.strictEqual(dump(content, { lineWidth: -1 }), '|-\n' + indent(content) + '\n'); + }); + + test('falls back to literal style when no lines are foldable', function () { + var content = [ prefix, ' ' + line, ' ' + line ].join('\n'); + + assert.strictEqual(dump(content), '|-\n' + indent(content) + '\n'); + }); + }); +}); diff --git a/encoding/yaml/__test__/units/empty-node-resolving.js b/encoding/yaml/__test__/units/empty-node-resolving.js new file mode 100644 index 000000000000..efc8836cd60b --- /dev/null +++ b/encoding/yaml/__test__/units/empty-node-resolving.js @@ -0,0 +1,75 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../lib/js-yaml'); + + +suite('Resolving explicit tags on empty nodes', function () { + test('!!binary', function () { + assert.throws(function () { yaml.load('!!binary'); }, yaml.YAMLException); + }); + + test('!!bool', function () { + assert.throws(function () { yaml.load('!!bool'); }, yaml.YAMLException); + }); + + test('!!float', function () { + assert.throws(function () { yaml.load('!!float'); }, yaml.YAMLException); + }); + + test('!!int', function () { + assert.throws(function () { yaml.load('!!int'); }, yaml.YAMLException); + }); + + test('!!map', function () { + assert.deepEqual(yaml.load('!!map'), {}); + }); + + test('!!merge', function () { + assert.doesNotThrow(function () { yaml.load('? !!merge\n: []'); }); + }); + + test('!!null', function () { + // Fetch null from an array to reduce chance that null is returned because of another bug + assert.strictEqual(yaml.load('- !!null')[0], null); + }); + + test('!!omap', function () { + assert.deepEqual(yaml.load('!!omap'), []); + }); + + test('!!pairs', function () { + assert.deepEqual(yaml.load('!!pairs'), []); + }); + + test('!!seq', function () { + assert.deepEqual(yaml.load('!!seq'), []); + }); + + test('!!set', function () { + assert.deepEqual(yaml.load('!!set'), {}); + }); + + test('!!str', function () { + assert.strictEqual(yaml.load('!!str'), ''); + }); + + test('!!timestamp', function () { + assert.throws(function () { yaml.load('!!timestamp'); }, yaml.YAMLException); + }); + + test('!!js/function', function () { + assert.throws(function () { yaml.load('!!js/function'); }, yaml.YAMLException); + }); + + test('!!js/regexp', function () { + assert.throws(function () { yaml.load('!!js/regexp'); }, yaml.YAMLException); + }); + + test('!!js/undefined', function () { + // Fetch undefined from an array to reduce chance that undefined is returned because of another bug + assert.strictEqual(yaml.load('- !!js/undefined')[0], undefined); + }); +}); diff --git a/encoding/yaml/__test__/units/is-negative-zero.js b/encoding/yaml/__test__/units/is-negative-zero.js new file mode 100644 index 000000000000..da78849237ef --- /dev/null +++ b/encoding/yaml/__test__/units/is-negative-zero.js @@ -0,0 +1,15 @@ +// 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. + +var assert = require('assert'); + +var isNegativeZero = require('../../lib/js-yaml/common').isNegativeZero; + + +test('isNegativeZero', function () { + assert(!isNegativeZero(0)); + assert(!isNegativeZero(0.0)); + assert(isNegativeZero(-0)); + assert(isNegativeZero(-0.0)); +}); diff --git a/encoding/yaml/__test__/units/mark.js b/encoding/yaml/__test__/units/mark.js new file mode 100644 index 000000000000..d1d9d84bd892 --- /dev/null +++ b/encoding/yaml/__test__/units/mark.js @@ -0,0 +1,45 @@ +// 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. + +var assert = require('assert'); +var path = require('path'); +var fs = require('fs'); +var Mark = require('../../lib/js-yaml/mark'); + + +test('Mark', function () { + var filepath = path.join(__dirname, 'mark.txt'), + filedata = fs.readFileSync(filepath, 'utf8'); + + filedata.split('---\n').slice(1).forEach(function (input) { + var index = 0, line = 0, column = 0, + mark, snippet, data, pointer, temp; + + assert(input.indexOf('*') >= 0); + + while (input[index] !== '*') { + if (input[index] === '\n') { + line += 1; + column = 0; + } else { + column += 1; + } + index += 1; + } + + mark = new Mark(filepath, input, index, line, column); + snippet = mark.getSnippet(2, 79); + + assert(typeof snippet, 'string'); + + temp = snippet.split('\n'); + assert.strictEqual(temp.length, 2); + + data = temp[0]; + pointer = temp[1]; + + assert(data.length < 82); + assert.strictEqual(data[pointer.length - 1], '*'); + }); +}); diff --git a/encoding/yaml/__test__/units/mark.txt b/encoding/yaml/__test__/units/mark.txt new file mode 100644 index 000000000000..7b08ee43e5fd --- /dev/null +++ b/encoding/yaml/__test__/units/mark.txt @@ -0,0 +1,38 @@ +--- +*The first line. +The last line. +--- +The first*line. +The last line. +--- +The first line.* +The last line. +--- +The first line. +*The last line. +--- +The first line. +The last*line. +--- +The first line. +The last line.* +--- +The first line. +*The selected line. +The last line. +--- +The first line. +The selected*line. +The last line. +--- +The first line. +The selected line.* +The last line. +--- +*The only line. +--- +The only*line. +--- +The only line.* +--- +Loooooooooooooooooooooooooooooooooooooooooooooong*Liiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiine diff --git a/encoding/yaml/__test__/units/parse-function-security.js b/encoding/yaml/__test__/units/parse-function-security.js new file mode 100644 index 000000000000..8ad194609098 --- /dev/null +++ b/encoding/yaml/__test__/units/parse-function-security.js @@ -0,0 +1,25 @@ +// 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. + +var assert = require('assert'); +var fs = require('fs'); +var path = require('path'); +var yaml = require('../../lib/js-yaml'); + + +var badThings = []; + + +global.makeBadThing = function (thing) { + badThings.push(thing); +}; + + +test('Function constructor must not allow to execute any code while parsing.', function () { + var filename = path.join(__dirname, 'parse-function-security.yml'), + contents = fs.readFileSync(filename, 'utf8'); + + assert.throws(function () { yaml.load(contents); }, yaml.YAMLException); + assert.deepEqual(badThings, []); +}); diff --git a/encoding/yaml/__test__/units/parse-function-security.yml b/encoding/yaml/__test__/units/parse-function-security.yml new file mode 100644 index 000000000000..812e9c9c67ae --- /dev/null +++ b/encoding/yaml/__test__/units/parse-function-security.yml @@ -0,0 +1,3 @@ +tests: + - !!js/function 'makeBadThing("BAD THING 1")' + - !!js/function 'function () { makeBadThing("BAD THING 2") }.call(this)' diff --git a/encoding/yaml/__test__/units/single-document-error.js b/encoding/yaml/__test__/units/single-document-error.js new file mode 100644 index 000000000000..b4a60f3dbdcc --- /dev/null +++ b/encoding/yaml/__test__/units/single-document-error.js @@ -0,0 +1,21 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +test('Loading multidocument source using `load` should cause an error', function () { + assert.throws(function () { + yaml.load('--- # first document\n--- # second document\n'); + }, yaml.YAMLException); + + assert.throws(function () { + yaml.load('---\nfoo: bar\n---\nfoo: bar\n'); + }, yaml.YAMLException); + + assert.throws(function () { + yaml.load('foo: bar\n---\nfoo: bar\n'); + }, yaml.YAMLException); +}); diff --git a/encoding/yaml/__test__/units/skip-invalid.js b/encoding/yaml/__test__/units/skip-invalid.js new file mode 100644 index 000000000000..35e78122cd5f --- /dev/null +++ b/encoding/yaml/__test__/units/skip-invalid.js @@ -0,0 +1,35 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + + +var sample = { + number: 42, + undef: undefined, + string: 'hello', + func: function (a, b) { return a + b; }, + regexp: /^hel+o/, + array: [ 1, 2, 3 ] +}; + + +var expected = { + number: 42, + string: 'hello', + array: [ 1, 2, 3 ] +}; + + +test('Dumper must throw an exception on invalid type when option `skipInvalid` is false.', function () { + assert.throws(function () { + yaml.safeDump(sample, { skipInvalid: false }); + }, yaml.YAMLException); +}); + + +test('Dumper must skip pairs and values with invalid types when option `skipInvalid` is true.', function () { + assert.deepEqual(yaml.load(yaml.safeDump(sample, { skipInvalid: true })), expected); +}); diff --git a/encoding/yaml/__test__/units/sort-keys.js b/encoding/yaml/__test__/units/sort-keys.js new file mode 100644 index 000000000000..eafc0f162731 --- /dev/null +++ b/encoding/yaml/__test__/units/sort-keys.js @@ -0,0 +1,27 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + +var sample = { b: 1, a: 2, c: 3 }; +var unsortedExpected = 'b: 1\na: 2\nc: 3\n'; +var simpleExpected = 'a: 2\nb: 1\nc: 3\n'; +var reverseExpected = 'c: 3\nb: 1\na: 2\n'; + +test('Dumper should sort preserve key insertion order', function () { + assert.deepEqual(yaml.safeDump(sample, { sortKeys: false }), unsortedExpected); +}); + +test('Dumper should sort keys when sortKeys is true', function () { + assert.deepEqual(yaml.safeDump(sample, { sortKeys: true }), simpleExpected); +}); + +test('Dumper should sort keys by sortKeys function when specified', function () { + assert.deepEqual(yaml.safeDump(sample, { + sortKeys: function (a, b) { + return a < b ? 1 : a > b ? -1 : 0; + } + }), reverseExpected); +}); diff --git a/encoding/yaml/__test__/units/tagmultikind.js b/encoding/yaml/__test__/units/tagmultikind.js new file mode 100644 index 000000000000..af306a854872 --- /dev/null +++ b/encoding/yaml/__test__/units/tagmultikind.js @@ -0,0 +1,40 @@ +// 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. + +var assert = require('assert'); +var yaml = require('../../'); + +var tags = [ { + tag: 'Include', + type: 'scalar' +}, { + tag: 'Include', + type: 'mapping' +} ].map(function (fn) { + return new yaml.Type('!' + fn.tag, { + kind: fn.type, + resolve: function () { + return true; + }, + construct: function (obj) { + return obj; + } + }); +}); + +var schema = yaml.Schema.create(tags); + + +test('Process tag with kind: scalar', function () { + assert.deepEqual(yaml.safeLoad('!Include foobar', { + schema: schema + }), 'foobar'); +}); + + +test('Process tag with kind: mapping', function () { + assert.deepEqual(yaml.safeLoad('!Include\n location: foobar', { + schema: schema + }), { location: 'foobar' }); +}); diff --git a/encoding/yaml/dumper/dumper.ts b/encoding/yaml/dumper/dumper.ts new file mode 100644 index 000000000000..5b15ba452052 --- /dev/null +++ b/encoding/yaml/dumper/dumper.ts @@ -0,0 +1,898 @@ +// 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. + +/* 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 = common.ArrayObject; + +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)[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 ""; +} diff --git a/encoding/yaml/dumper/dumper_state.ts b/encoding/yaml/dumper/dumper_state.ts new file mode 100644 index 000000000000..c1d7f5fef50f --- /dev/null +++ b/encoding/yaml/dumper/dumper_state.ts @@ -0,0 +1,137 @@ +// 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 { 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 | null +): ArrayObject { + if (typeof map === "undefined" || map === null) return {}; + + let type: Type; + const result: ArrayObject = {}; + 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 | 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: string = ""; + public duplicates: Any[] = []; + public usedDuplicates: Any[] = []; // changed from null to [] + public styleMap: ArrayObject; + 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; + } +} diff --git a/encoding/yaml/error.ts b/encoding/yaml/error.ts new file mode 100644 index 000000000000..10420ea29cf7 --- /dev/null +++ b/encoding/yaml/error.ts @@ -0,0 +1,21 @@ +// 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 { Mark } from "./mark.ts"; + +const { DenoError, ErrorKind } = Deno; + +export class YAMLError extends DenoError { + 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}`; + } +} diff --git a/encoding/yaml/example/dump.ts b/encoding/yaml/example/dump.ts new file mode 100644 index 000000000000..c4282d657ec3 --- /dev/null +++ b/encoding/yaml/example/dump.ts @@ -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" + }) +); diff --git a/encoding/yaml/example/inout.ts b/encoding/yaml/example/inout.ts new file mode 100644 index 000000000000..6a52d808b4b0 --- /dev/null +++ b/encoding/yaml/example/inout.ts @@ -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."); +} diff --git a/encoding/yaml/example/parse.ts b/encoding/yaml/example/parse.ts new file mode 100644 index 000000000000..31d4c88773be --- /dev/null +++ b/encoding/yaml/example/parse.ts @@ -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); +} diff --git a/encoding/yaml/example/sample_document.ts b/encoding/yaml/example/sample_document.ts new file mode 100644 index 000000000000..7b426c142798 --- /dev/null +++ b/encoding/yaml/example/sample_document.ts @@ -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++; + } +})(); diff --git a/encoding/yaml/example/sample_document.yml b/encoding/yaml/example/sample_document.yml new file mode 100644 index 000000000000..1f3c2eb3e4d6 --- /dev/null +++ b/encoding/yaml/example/sample_document.yml @@ -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!'; +# } diff --git a/encoding/yaml/loader/loader.ts b/encoding/yaml/loader/loader.ts new file mode 100644 index 000000000000..716b928f84f3 --- /dev/null +++ b/encoding/yaml/loader/loader.ts @@ -0,0 +1,1814 @@ +// 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. + +/* eslint-disable no-conditional-assignment */ +/* eslint-disable max-len */ + +import { YAMLError } from "../error.ts"; +import { Mark } from "../mark.ts"; +import { Type } from "../type.ts"; +import * as common from "../utils.ts"; +import { LoaderState, LoaderStateOptions, ResultType } from "./loader_state.ts"; + +type Any = common.Any; +type ArrayObject = common.ArrayObject; + +const _hasOwnProperty = Object.prototype.hasOwnProperty; + +const CONTEXT_FLOW_IN = 1; +const CONTEXT_FLOW_OUT = 2; +const CONTEXT_BLOCK_IN = 3; +const CONTEXT_BLOCK_OUT = 4; + +const CHOMPING_CLIP = 1; +const CHOMPING_STRIP = 2; +const CHOMPING_KEEP = 3; + +const PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; +const PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; +const PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; +const PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; +/* eslint-disable-next-line max-len */ +const PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; + +function _class(obj: Any): string { + return Object.prototype.toString.call(obj); +} + +function isEOL(c: number): boolean { + return c === 0x0a /* LF */ || c === 0x0d /* CR */; +} + +function isWhiteSpace(c: number): boolean { + return c === 0x09 /* Tab */ || c === 0x20 /* Space */; +} + +function isWsOrEol(c: number): boolean { + return ( + c === 0x09 /* Tab */ || + c === 0x20 /* Space */ || + c === 0x0a /* LF */ || + c === 0x0d /* CR */ + ); +} + +function isFlowIndicator(c: number): boolean { + return ( + c === 0x2c /* , */ || + c === 0x5b /* [ */ || + c === 0x5d /* ] */ || + c === 0x7b /* { */ || + c === 0x7d /* } */ + ); +} + +function fromHexCode(c: number): number { + if (0x30 /* 0 */ <= c && c <= 0x39 /* 9 */) { + return c - 0x30; + } + + const lc = c | 0x20; + + if (0x61 /* a */ <= lc && lc <= 0x66 /* f */) { + return lc - 0x61 + 10; + } + + return -1; +} + +function escapedHexLen(c: number): number { + if (c === 0x78 /* x */) { + return 2; + } + if (c === 0x75 /* u */) { + return 4; + } + if (c === 0x55 /* U */) { + return 8; + } + return 0; +} + +function fromDecimalCode(c: number): number { + if (0x30 /* 0 */ <= c && c <= 0x39 /* 9 */) { + return c - 0x30; + } + + return -1; +} + +function simpleEscapeSequence(c: number): string { + /* eslint:disable:prettier */ + return c === 0x30 /* 0 */ + ? "\x00" + : c === 0x61 /* a */ + ? "\x07" + : c === 0x62 /* b */ + ? "\x08" + : c === 0x74 /* t */ + ? "\x09" + : c === 0x09 /* Tab */ + ? "\x09" + : c === 0x6e /* n */ + ? "\x0A" + : c === 0x76 /* v */ + ? "\x0B" + : c === 0x66 /* f */ + ? "\x0C" + : c === 0x72 /* r */ + ? "\x0D" + : c === 0x65 /* e */ + ? "\x1B" + : c === 0x20 /* Space */ + ? " " + : c === 0x22 /* " */ + ? "\x22" + : c === 0x2f /* / */ + ? "/" + : c === 0x5c /* \ */ + ? "\x5C" + : c === 0x4e /* N */ + ? "\x85" + : c === 0x5f /* _ */ + ? "\xA0" + : c === 0x4c /* L */ + ? "\u2028" + : c === 0x50 /* P */ + ? "\u2029" + : ""; + /* eslint:enable:prettier */ +} + +function charFromCodepoint(c: number): string { + if (c <= 0xffff) { + return String.fromCharCode(c); + } + // Encode UTF-16 surrogate pair + // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF + return String.fromCharCode( + ((c - 0x010000) >> 10) + 0xd800, + ((c - 0x010000) & 0x03ff) + 0xdc00 + ); +} + +const simpleEscapeCheck = new Array(256); // integer, for fast access +const simpleEscapeMap = new Array(256); +for (let i = 0; i < 256; i++) { + simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; + simpleEscapeMap[i] = simpleEscapeSequence(i); +} + +function generateError(state: LoaderState, message: string): YAMLError { + return new YAMLError( + message, + new Mark( + state.filename as string, + state.input, + state.position, + state.line, + state.position - state.lineStart + ) + ); +} + +function throwError(state: LoaderState, message: string): never { + throw generateError(state, message); +} + +function throwWarning(state: LoaderState, message: string): void { + if (state.onWarning) { + state.onWarning.call(null, generateError(state, message)); + } +} + +interface DirectiveHandlers { + [directive: string]: ( + state: LoaderState, + name: string, + ...args: Any[] + ) => void; +} + +const directiveHandlers: DirectiveHandlers = { + YAML(state, name, ...args): void { + if (state.version !== null) { + return throwError(state, "duplication of %YAML directive"); + } + + if (args.length !== 1) { + return throwError(state, "YAML directive accepts exactly one argument"); + } + + let match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); + + if (match === null) { + return throwError(state, "ill-formed argument of the YAML directive"); + } + + let major = parseInt(match[1], 10); + let minor = parseInt(match[2], 10); + + if (major !== 1) { + return throwError(state, "unacceptable YAML version of the document"); + } + + state.version = args[0]; + state.checkLineBreaks = minor < 2; + + if (minor !== 1 && minor !== 2) { + return throwWarning(state, "unsupported YAML version of the document"); + } + }, + + TAG(state, name, ...args): void { + let handle, prefix; + + if (args.length !== 2) { + return throwError(state, "TAG directive accepts exactly two arguments"); + } + + handle = args[0]; + prefix = args[1]; + + if (!PATTERN_TAG_HANDLE.test(handle)) { + return throwError( + state, + "ill-formed tag handle (first argument) of the TAG directive" + ); + } + + if (_hasOwnProperty.call(state.tagMap, handle)) { + return throwError( + state, + `there is a previously declared suffix for "${handle}" tag handle` + ); + } + + if (!PATTERN_TAG_URI.test(prefix)) { + return throwError( + state, + "ill-formed tag prefix (second argument) of the TAG directive" + ); + } + + if (typeof state.tagMap === "undefined") { + state.tagMap = {}; + } + state.tagMap[handle] = prefix; + } +}; + +function captureSegment( + state: LoaderState, + start: number, + end: number, + checkJson: boolean +): void { + let result: string; + if (start < end) { + result = state.input.slice(start, end); + + if (checkJson) { + for ( + let position = 0, length = result.length; + position < length; + position++ + ) { + const character = result.charCodeAt(position); + if ( + !(character === 0x09 || (0x20 <= character && character <= 0x10ffff)) + ) { + return throwError(state, "expected valid JSON character"); + } + } + } else if (PATTERN_NON_PRINTABLE.test(result)) { + return throwError(state, "the stream contains non-printable characters"); + } + + state.result += result; + } +} + +function mergeMappings( + state: LoaderState, + destination: ArrayObject, + source: ArrayObject, + overridableKeys: ArrayObject +): void { + if (!common.isObject(source)) { + return throwError( + state, + "cannot merge mappings; the provided source object is unacceptable" + ); + } + + const keys = Object.keys(source); + for (let i = 0, len = keys.length; i < len; i++) { + const key = keys[i]; + if (!_hasOwnProperty.call(destination, key)) { + destination[key] = (source as ArrayObject)[key]; + overridableKeys[key] = true; + } + } +} + +function storeMappingPair( + state: LoaderState, + result: ArrayObject | null, + overridableKeys: ArrayObject, + keyTag: string | null, + keyNode: Any, + valueNode: Any, + startLine?: number, + startPos?: number +): ArrayObject { + // The output is a plain object here, so keys can only be strings. + // We need to convert keyNode to a string, but doing so can hang the process + // (deeply nested arrays that explode exponentially using aliases). + if (Array.isArray(keyNode)) { + keyNode = Array.prototype.slice.call(keyNode); + + for (let index = 0, quantity = keyNode.length; index < quantity; index++) { + if (Array.isArray(keyNode[index])) { + return throwError(state, "nested arrays are not supported inside keys"); + } + + if ( + typeof keyNode === "object" && + _class(keyNode[index]) === "[object Object]" + ) { + keyNode[index] = "[object Object]"; + } + } + } + + // Avoid code execution in load() via toString property + // (still use its own toString for arrays, timestamps, + // and whatever user schema extensions happen to have @@toStringTag) + if (typeof keyNode === "object" && _class(keyNode) === "[object Object]") { + keyNode = "[object Object]"; + } + + keyNode = String(keyNode); + + if (result === null) { + result = {}; + } + + if (keyTag === "tag:yaml.org,2002:merge") { + if (Array.isArray(valueNode)) { + for ( + let index = 0, quantity = valueNode.length; + index < quantity; + index++ + ) { + mergeMappings(state, result, valueNode[index], overridableKeys); + } + } else { + mergeMappings(state, result, valueNode, overridableKeys); + } + } else { + if ( + !state.json && + !_hasOwnProperty.call(overridableKeys, keyNode) && + _hasOwnProperty.call(result, keyNode) + ) { + state.line = startLine || state.line; + state.position = startPos || state.position; + return throwError(state, "duplicated mapping key"); + } + result[keyNode] = valueNode; + delete overridableKeys[keyNode]; + } + + return result; +} + +function readLineBreak(state: LoaderState): void { + let ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x0a /* LF */) { + state.position++; + } else if (ch === 0x0d /* CR */) { + state.position++; + if (state.input.charCodeAt(state.position) === 0x0a /* LF */) { + state.position++; + } + } else { + return throwError(state, "a line break is expected"); + } + + state.line += 1; + state.lineStart = state.position; +} + +function skipSeparationSpace( + state: LoaderState, + allowComments: boolean, + checkIndent: number +): number { + let lineBreaks = 0, + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + while (isWhiteSpace(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (allowComments && ch === 0x23 /* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0x0a /* LF */ && ch !== 0x0d /* CR */ && ch !== 0); + } + + if (isEOL(ch)) { + readLineBreak(state); + + ch = state.input.charCodeAt(state.position); + lineBreaks++; + state.lineIndent = 0; + + while (ch === 0x20 /* Space */) { + state.lineIndent++; + ch = state.input.charCodeAt(++state.position); + } + } else { + break; + } + } + + if ( + checkIndent !== -1 && + lineBreaks !== 0 && + state.lineIndent < checkIndent + ) { + throwWarning(state, "deficient indentation"); + } + + return lineBreaks; +} + +function testDocumentSeparator(state: LoaderState): boolean { + let _position = state.position; + let ch = state.input.charCodeAt(_position); + + // Condition state.position === state.lineStart is tested + // in parent on each call, for efficiency. No needs to test here again. + if ( + (ch === 0x2d /* - */ || ch === 0x2e) /* . */ && + ch === state.input.charCodeAt(_position + 1) && + ch === state.input.charCodeAt(_position + 2) + ) { + _position += 3; + + ch = state.input.charCodeAt(_position); + + if (ch === 0 || isWsOrEol(ch)) { + return true; + } + } + + return false; +} + +function writeFoldedLines(state: LoaderState, count: number): void { + if (count === 1) { + state.result += " "; + } else if (count > 1) { + state.result += common.repeat("\n", count - 1); + } +} + +function readPlainScalar( + state: LoaderState, + nodeIndent: number, + withinFlowCollection: boolean +): boolean { + const kind = state.kind; + const result = state.result; + let ch = state.input.charCodeAt(state.position); + + if ( + isWsOrEol(ch) || + isFlowIndicator(ch) || + ch === 0x23 /* # */ || + ch === 0x26 /* & */ || + ch === 0x2a /* * */ || + ch === 0x21 /* ! */ || + ch === 0x7c /* | */ || + ch === 0x3e /* > */ || + ch === 0x27 /* ' */ || + ch === 0x22 /* " */ || + ch === 0x25 /* % */ || + ch === 0x40 /* @ */ || + ch === 0x60 /* ` */ + ) { + return false; + } + + let following: number; + if (ch === 0x3f /* ? */ || ch === 0x2d /* - */) { + following = state.input.charCodeAt(state.position + 1); + + if ( + isWsOrEol(following) || + (withinFlowCollection && isFlowIndicator(following)) + ) { + return false; + } + } + + state.kind = "scalar"; + state.result = ""; + let captureEnd: number, + captureStart = (captureEnd = state.position); + let hasPendingContent = false; + let line = 0; + while (ch !== 0) { + if (ch === 0x3a /* : */) { + following = state.input.charCodeAt(state.position + 1); + + if ( + isWsOrEol(following) || + (withinFlowCollection && isFlowIndicator(following)) + ) { + break; + } + } else if (ch === 0x23 /* # */) { + const preceding = state.input.charCodeAt(state.position - 1); + + if (isWsOrEol(preceding)) { + break; + } + } else if ( + (state.position === state.lineStart && testDocumentSeparator(state)) || + (withinFlowCollection && isFlowIndicator(ch)) + ) { + break; + } else if (isEOL(ch)) { + line = state.line; + const lineStart = state.lineStart; + const lineIndent = state.lineIndent; + skipSeparationSpace(state, false, -1); + + if (state.lineIndent >= nodeIndent) { + hasPendingContent = true; + ch = state.input.charCodeAt(state.position); + continue; + } else { + state.position = captureEnd; + state.line = line; + state.lineStart = lineStart; + state.lineIndent = lineIndent; + break; + } + } + + if (hasPendingContent) { + captureSegment(state, captureStart, captureEnd, false); + writeFoldedLines(state, state.line - line); + captureStart = captureEnd = state.position; + hasPendingContent = false; + } + + if (!isWhiteSpace(ch)) { + captureEnd = state.position + 1; + } + + ch = state.input.charCodeAt(++state.position); + } + + captureSegment(state, captureStart, captureEnd, false); + + if (!common.isNullOrUndefined(state.result)) { + return true; + } + + state.kind = kind; + state.result = result; + return false; +} + +function readSingleQuotedScalar( + state: LoaderState, + nodeIndent: number +): boolean { + let ch, captureStart, captureEnd; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x27 /* ' */) { + return false; + } + + state.kind = "scalar"; + state.result = ""; + state.position++; + captureStart = captureEnd = state.position; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + if (ch === 0x27 /* ' */) { + captureSegment(state, captureStart, state.position, true); + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x27 /* ' */) { + captureStart = state.position; + state.position++; + captureEnd = state.position; + } else { + return true; + } + } else if (isEOL(ch)) { + captureSegment(state, captureStart, captureEnd, true); + writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); + captureStart = captureEnd = state.position; + } else if ( + state.position === state.lineStart && + testDocumentSeparator(state) + ) { + return throwError( + state, + "unexpected end of the document within a single quoted scalar" + ); + } else { + state.position++; + captureEnd = state.position; + } + } + + return throwError( + state, + "unexpected end of the stream within a single quoted scalar" + ); +} + +function readDoubleQuotedScalar( + state: LoaderState, + nodeIndent: number +): boolean { + let ch = state.input.charCodeAt(state.position); + + if (ch !== 0x22 /* " */) { + return false; + } + + state.kind = "scalar"; + state.result = ""; + state.position++; + let captureEnd: number, + captureStart = (captureEnd = state.position); + let tmp: number; + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + if (ch === 0x22 /* " */) { + captureSegment(state, captureStart, state.position, true); + state.position++; + return true; + } + if (ch === 0x5c /* \ */) { + captureSegment(state, captureStart, state.position, true); + ch = state.input.charCodeAt(++state.position); + + if (isEOL(ch)) { + skipSeparationSpace(state, false, nodeIndent); + + // TODO: rework to inline fn with no type cast? + } else if (ch < 256 && simpleEscapeCheck[ch]) { + state.result += simpleEscapeMap[ch]; + state.position++; + } else if ((tmp = escapedHexLen(ch)) > 0) { + let hexLength = tmp; + let hexResult = 0; + + for (; hexLength > 0; hexLength--) { + ch = state.input.charCodeAt(++state.position); + + if ((tmp = fromHexCode(ch)) >= 0) { + hexResult = (hexResult << 4) + tmp; + } else { + return throwError(state, "expected hexadecimal character"); + } + } + + state.result += charFromCodepoint(hexResult); + + state.position++; + } else { + return throwError(state, "unknown escape sequence"); + } + + captureStart = captureEnd = state.position; + } else if (isEOL(ch)) { + captureSegment(state, captureStart, captureEnd, true); + writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); + captureStart = captureEnd = state.position; + } else if ( + state.position === state.lineStart && + testDocumentSeparator(state) + ) { + return throwError( + state, + "unexpected end of the document within a double quoted scalar" + ); + } else { + state.position++; + captureEnd = state.position; + } + } + + return throwError( + state, + "unexpected end of the stream within a double quoted scalar" + ); +} + +function readFlowCollection(state: LoaderState, nodeIndent: number): boolean { + let ch = state.input.charCodeAt(state.position); + let terminator: number; + let isMapping = true; + let result: ResultType = {}; + if (ch === 0x5b /* [ */) { + terminator = 0x5d; /* ] */ + isMapping = false; + result = []; + } else if (ch === 0x7b /* { */) { + terminator = 0x7d; /* } */ + } else { + return false; + } + + if ( + state.anchor !== null && + typeof state.anchor != "undefined" && + typeof state.anchorMap != "undefined" + ) { + state.anchorMap[state.anchor] = result; + } + + ch = state.input.charCodeAt(++state.position); + + const tag = state.tag, + anchor = state.anchor; + let readNext = true; + let valueNode, + keyNode, + keyTag: string | null = (keyNode = valueNode = null), + isExplicitPair: boolean, + isPair = (isExplicitPair = false); + let following = 0, + line = 0; + const overridableKeys: ArrayObject = {}; + while (ch !== 0) { + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if (ch === terminator) { + state.position++; + state.tag = tag; + state.anchor = anchor; + state.kind = isMapping ? "mapping" : "sequence"; + state.result = result; + return true; + } + if (!readNext) { + return throwError(state, "missed comma between flow collection entries"); + } + + keyTag = keyNode = valueNode = null; + isPair = isExplicitPair = false; + + if (ch === 0x3f /* ? */) { + following = state.input.charCodeAt(state.position + 1); + + if (isWsOrEol(following)) { + isPair = isExplicitPair = true; + state.position++; + skipSeparationSpace(state, true, nodeIndent); + } + } + + line = state.line; + // eslint-disable-next-line @typescript-eslint/no-use-before-define + composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + keyTag = state.tag || null; + keyNode = state.result; + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if ((isExplicitPair || state.line === line) && ch === 0x3a /* : */) { + isPair = true; + ch = state.input.charCodeAt(++state.position); + skipSeparationSpace(state, true, nodeIndent); + // eslint-disable-next-line @typescript-eslint/no-use-before-define + composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + valueNode = state.result; + } + + if (isMapping) { + storeMappingPair( + state, + result, + overridableKeys, + keyTag, + keyNode, + valueNode + ); + } else if (isPair) { + (result as Array<{}>).push( + storeMappingPair( + state, + null, + overridableKeys, + keyTag, + keyNode, + valueNode + ) + ); + } else { + (result as ResultType[]).push(keyNode as ResultType); + } + + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x2c /* , */) { + readNext = true; + ch = state.input.charCodeAt(++state.position); + } else { + readNext = false; + } + } + + return throwError( + state, + "unexpected end of the stream within a flow collection" + ); +} + +function readBlockScalar(state: LoaderState, nodeIndent: number): boolean { + let chomping = CHOMPING_CLIP, + didReadContent = false, + detectedIndent = false, + textIndent = nodeIndent, + emptyLines = 0, + atMoreIndented = false; + + let ch = state.input.charCodeAt(state.position); + + let folding = false; + if (ch === 0x7c /* | */) { + folding = false; + } else if (ch === 0x3e /* > */) { + folding = true; + } else { + return false; + } + + state.kind = "scalar"; + state.result = ""; + + let tmp = 0; + while (ch !== 0) { + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x2b /* + */ || ch === 0x2d /* - */) { + if (CHOMPING_CLIP === chomping) { + chomping = ch === 0x2b /* + */ ? CHOMPING_KEEP : CHOMPING_STRIP; + } else { + return throwError(state, "repeat of a chomping mode identifier"); + } + } else if ((tmp = fromDecimalCode(ch)) >= 0) { + if (tmp === 0) { + return throwError( + state, + "bad explicit indentation width of a block scalar; it cannot be less than one" + ); + } else if (!detectedIndent) { + textIndent = nodeIndent + tmp - 1; + detectedIndent = true; + } else { + return throwError(state, "repeat of an indentation width identifier"); + } + } else { + break; + } + } + + if (isWhiteSpace(ch)) { + do { + ch = state.input.charCodeAt(++state.position); + } while (isWhiteSpace(ch)); + + if (ch === 0x23 /* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (!isEOL(ch) && ch !== 0); + } + } + + while (ch !== 0) { + readLineBreak(state); + state.lineIndent = 0; + + ch = state.input.charCodeAt(state.position); + + while ( + (!detectedIndent || state.lineIndent < textIndent) && + ch === 0x20 /* Space */ + ) { + state.lineIndent++; + ch = state.input.charCodeAt(++state.position); + } + + if (!detectedIndent && state.lineIndent > textIndent) { + textIndent = state.lineIndent; + } + + if (isEOL(ch)) { + emptyLines++; + continue; + } + + // End of the scalar. + if (state.lineIndent < textIndent) { + // Perform the chomping. + if (chomping === CHOMPING_KEEP) { + state.result += common.repeat( + "\n", + didReadContent ? 1 + emptyLines : emptyLines + ); + } else if (chomping === CHOMPING_CLIP) { + if (didReadContent) { + // i.e. only if the scalar is not empty. + state.result += "\n"; + } + } + + // Break this `while` cycle and go to the funciton's epilogue. + break; + } + + // Folded style: use fancy rules to handle line breaks. + if (folding) { + // Lines starting with white space characters (more-indented lines) are not folded. + if (isWhiteSpace(ch)) { + atMoreIndented = true; + // except for the first content line (cf. Example 8.1) + state.result += common.repeat( + "\n", + didReadContent ? 1 + emptyLines : emptyLines + ); + + // End of more-indented block. + } else if (atMoreIndented) { + atMoreIndented = false; + state.result += common.repeat("\n", emptyLines + 1); + + // Just one line break - perceive as the same line. + } else if (emptyLines === 0) { + if (didReadContent) { + // i.e. only if we have already read some scalar content. + state.result += " "; + } + + // Several line breaks - perceive as different lines. + } else { + state.result += common.repeat("\n", emptyLines); + } + + // Literal style: just add exact number of line breaks between content lines. + } else { + // Keep all line breaks except the header line break. + state.result += common.repeat( + "\n", + didReadContent ? 1 + emptyLines : emptyLines + ); + } + + didReadContent = true; + detectedIndent = true; + emptyLines = 0; + const captureStart = state.position; + + while (!isEOL(ch) && ch !== 0) { + ch = state.input.charCodeAt(++state.position); + } + + captureSegment(state, captureStart, state.position, false); + } + + return true; +} + +function readBlockSequence(state: LoaderState, nodeIndent: number): boolean { + let line: number, + following: number, + detected = false, + ch: number; + const tag = state.tag, + anchor = state.anchor, + result: Any[] = []; + + if ( + state.anchor !== null && + typeof state.anchor !== "undefined" && + typeof state.anchorMap !== "undefined" + ) { + state.anchorMap[state.anchor] = result; + } + + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + if (ch !== 0x2d /* - */) { + break; + } + + following = state.input.charCodeAt(state.position + 1); + + if (!isWsOrEol(following)) { + break; + } + + detected = true; + state.position++; + + if (skipSeparationSpace(state, true, -1)) { + if (state.lineIndent <= nodeIndent) { + result.push(null); + ch = state.input.charCodeAt(state.position); + continue; + } + } + + line = state.line; + // eslint-disable-next-line @typescript-eslint/no-use-before-define + composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); + result.push(state.result); + skipSeparationSpace(state, true, -1); + + ch = state.input.charCodeAt(state.position); + + if ((state.line === line || state.lineIndent > nodeIndent) && ch !== 0) { + return throwError(state, "bad indentation of a sequence entry"); + } else if (state.lineIndent < nodeIndent) { + break; + } + } + + if (detected) { + state.tag = tag; + state.anchor = anchor; + state.kind = "sequence"; + state.result = result; + return true; + } + return false; +} + +function readBlockMapping( + state: LoaderState, + nodeIndent: number, + flowIndent: number +): boolean { + const tag = state.tag, + anchor = state.anchor, + result = {}, + overridableKeys = {}; + let following: number, + allowCompact = false, + line: number, + pos: number, + keyTag = null, + keyNode = null, + valueNode = null, + atExplicitKey = false, + detected = false, + ch: number; + + if ( + state.anchor !== null && + typeof state.anchor !== "undefined" && + typeof state.anchorMap !== "undefined" + ) { + state.anchorMap[state.anchor] = result; + } + + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + following = state.input.charCodeAt(state.position + 1); + line = state.line; // Save the current line. + pos = state.position; + + // + // Explicit notation case. There are two separate blocks: + // first for the key (denoted by "?") and second for the value (denoted by ":") + // + if ((ch === 0x3f /* ? */ || ch === 0x3a) /* : */ && isWsOrEol(following)) { + if (ch === 0x3f /* ? */) { + if (atExplicitKey) { + storeMappingPair( + state, + result, + overridableKeys, + keyTag as string, + keyNode, + null + ); + keyTag = keyNode = valueNode = null; + } + + detected = true; + atExplicitKey = true; + allowCompact = true; + } else if (atExplicitKey) { + // i.e. 0x3A/* : */ === character after the explicit key. + atExplicitKey = false; + allowCompact = true; + } else { + return throwError( + state, + "incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line" + ); + } + + state.position += 1; + ch = following; + + // + // Implicit notation case. Flow-style node as the key first, then ":", and the value. + // + // eslint-disable-next-line @typescript-eslint/no-use-before-define + } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { + if (state.line === line) { + ch = state.input.charCodeAt(state.position); + + while (isWhiteSpace(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (ch === 0x3a /* : */) { + ch = state.input.charCodeAt(++state.position); + + if (!isWsOrEol(ch)) { + return throwError( + state, + "a whitespace character is expected after the key-value separator within a block mapping" + ); + } + + if (atExplicitKey) { + storeMappingPair( + state, + result, + overridableKeys, + keyTag as string, + keyNode, + null + ); + keyTag = keyNode = valueNode = null; + } + + detected = true; + atExplicitKey = false; + allowCompact = false; + keyTag = state.tag; + keyNode = state.result; + } else if (detected) { + return throwError( + state, + "can not read an implicit mapping pair; a colon is missed" + ); + } else { + state.tag = tag; + state.anchor = anchor; + return true; // Keep the result of `composeNode`. + } + } else if (detected) { + return throwError( + state, + "can not read a block mapping entry; a multiline key may not be an implicit key" + ); + } else { + state.tag = tag; + state.anchor = anchor; + return true; // Keep the result of `composeNode`. + } + } else { + break; // Reading is done. Go to the epilogue. + } + + // + // Common reading code for both explicit and implicit notations. + // + if (state.line === line || state.lineIndent > nodeIndent) { + if ( + // eslint-disable-next-line @typescript-eslint/no-use-before-define + composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact) + ) { + if (atExplicitKey) { + keyNode = state.result; + } else { + valueNode = state.result; + } + } + + if (!atExplicitKey) { + storeMappingPair( + state, + result, + overridableKeys, + keyTag as string, + keyNode, + valueNode, + line, + pos + ); + keyTag = keyNode = valueNode = null; + } + + skipSeparationSpace(state, true, -1); + ch = state.input.charCodeAt(state.position); + } + + if (state.lineIndent > nodeIndent && ch !== 0) { + return throwError(state, "bad indentation of a mapping entry"); + } else if (state.lineIndent < nodeIndent) { + break; + } + } + + // + // Epilogue. + // + + // Special case: last mapping's node contains only the key in explicit notation. + if (atExplicitKey) { + storeMappingPair( + state, + result, + overridableKeys, + keyTag as string, + keyNode, + null + ); + } + + // Expose the resulting mapping. + if (detected) { + state.tag = tag; + state.anchor = anchor; + state.kind = "mapping"; + state.result = result; + } + + return detected; +} + +function readTagProperty(state: LoaderState): boolean { + let position: number, + isVerbatim = false, + isNamed = false, + tagHandle = "", + tagName: string, + ch: number; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x21 /* ! */) return false; + + if (state.tag !== null) { + return throwError(state, "duplication of a tag property"); + } + + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x3c /* < */) { + isVerbatim = true; + ch = state.input.charCodeAt(++state.position); + } else if (ch === 0x21 /* ! */) { + isNamed = true; + tagHandle = "!!"; + ch = state.input.charCodeAt(++state.position); + } else { + tagHandle = "!"; + } + + position = state.position; + + if (isVerbatim) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0 && ch !== 0x3e /* > */); + + if (state.position < state.length) { + tagName = state.input.slice(position, state.position); + ch = state.input.charCodeAt(++state.position); + } else { + return throwError( + state, + "unexpected end of the stream within a verbatim tag" + ); + } + } else { + while (ch !== 0 && !isWsOrEol(ch)) { + if (ch === 0x21 /* ! */) { + if (!isNamed) { + tagHandle = state.input.slice(position - 1, state.position + 1); + + if (!PATTERN_TAG_HANDLE.test(tagHandle)) { + return throwError( + state, + "named tag handle cannot contain such characters" + ); + } + + isNamed = true; + position = state.position + 1; + } else { + return throwError( + state, + "tag suffix cannot contain exclamation marks" + ); + } + } + + ch = state.input.charCodeAt(++state.position); + } + + tagName = state.input.slice(position, state.position); + + if (PATTERN_FLOW_INDICATORS.test(tagName)) { + return throwError( + state, + "tag suffix cannot contain flow indicator characters" + ); + } + } + + if (tagName && !PATTERN_TAG_URI.test(tagName)) { + return throwError( + state, + `tag name cannot contain such characters: ${tagName}` + ); + } + + if (isVerbatim) { + state.tag = tagName; + } else if ( + typeof state.tagMap !== "undefined" && + _hasOwnProperty.call(state.tagMap, tagHandle) + ) { + state.tag = state.tagMap[tagHandle] + tagName; + } else if (tagHandle === "!") { + state.tag = `!${tagName}`; + } else if (tagHandle === "!!") { + state.tag = `tag:yaml.org,2002:${tagName}`; + } else { + return throwError(state, `undeclared tag handle "${tagHandle}"`); + } + + return true; +} + +function readAnchorProperty(state: LoaderState): boolean { + let position: number, ch: number; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x26 /* & */) return false; + + if (state.anchor !== null) { + return throwError(state, "duplication of an anchor property"); + } + + ch = state.input.charCodeAt(++state.position); + position = state.position; + + while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (state.position === position) { + return throwError( + state, + "name of an anchor node must contain at least one character" + ); + } + + state.anchor = state.input.slice(position, state.position); + return true; +} + +function readAlias(state: LoaderState): boolean { + let _position, alias, ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x2a /* * */) return false; + + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !isWsOrEol(ch) && !isFlowIndicator(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (state.position === _position) { + return throwError( + state, + "name of an alias node must contain at least one character" + ); + } + + alias = state.input.slice(_position, state.position); + + if ( + typeof state.anchorMap !== "undefined" && + !state.anchorMap.hasOwnProperty(alias) + ) { + return throwError(state, `unidentified alias "${alias}"`); + } + + if (typeof state.anchorMap !== "undefined") { + state.result = state.anchorMap[alias]; + } + skipSeparationSpace(state, true, -1); + return true; +} + +function composeNode( + state: LoaderState, + parentIndent: number, + nodeContext: number, + allowToSeek: boolean, + allowCompact: boolean +): boolean { + let allowBlockStyles: boolean, + allowBlockScalars: boolean, + allowBlockCollections: boolean, + indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) { + indentStatus = 1; + } else if (state.lineIndent === parentIndent) { + indentStatus = 0; + } else if (state.lineIndent < parentIndent) { + indentStatus = -1; + } + } + } + + if (indentStatus === 1) { + while (readTagProperty(state) || readAnchorProperty(state)) { + if (skipSeparationSpace(state, true, -1)) { + atNewLine = true; + allowBlockCollections = allowBlockStyles; + + if (state.lineIndent > parentIndent) { + indentStatus = 1; + } else if (state.lineIndent === parentIndent) { + indentStatus = 0; + } else if (state.lineIndent < parentIndent) { + indentStatus = -1; + } + } else { + allowBlockCollections = false; + } + } + } + + if (allowBlockCollections) { + allowBlockCollections = atNewLine || allowCompact; + } + + if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { + const cond = + CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext; + flowIndent = cond ? parentIndent : parentIndent + 1; + + blockIndent = state.position - state.lineStart; + + if (indentStatus === 1) { + if ( + (allowBlockCollections && + (readBlockSequence(state, blockIndent) || + readBlockMapping(state, blockIndent, flowIndent))) || + readFlowCollection(state, flowIndent) + ) { + hasContent = true; + } else { + if ( + (allowBlockScalars && readBlockScalar(state, flowIndent)) || + readSingleQuotedScalar(state, flowIndent) || + readDoubleQuotedScalar(state, flowIndent) + ) { + hasContent = true; + } else if (readAlias(state)) { + hasContent = true; + + if (state.tag !== null || state.anchor !== null) { + return throwError( + state, + "alias node should not have Any properties" + ); + } + } else if ( + readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext) + ) { + hasContent = true; + + if (state.tag === null) { + state.tag = "?"; + } + } + + if (state.anchor !== null && typeof state.anchorMap !== "undefined") { + state.anchorMap[state.anchor] = state.result; + } + } + } else if (indentStatus === 0) { + // Special case: block sequences are allowed to have same indentation level as the parent. + // http://www.yaml.org/spec/1.2/spec.html#id2799784 + hasContent = + allowBlockCollections && readBlockSequence(state, blockIndent); + } + } + + if (state.tag !== null && state.tag !== "!") { + if (state.tag === "?") { + for ( + let typeIndex = 0, typeQuantity = state.implicitTypes.length; + typeIndex < typeQuantity; + typeIndex++ + ) { + type = state.implicitTypes[typeIndex]; + + // Implicit resolving is not allowed for non-scalar types, and '?' + // non-specific tag is only assigned to plain scalars. So, it isn't + // needed to check for 'kind' conformity. + + if (type.resolve(state.result)) { + // `state.result` updated in resolver if matched + state.result = type.construct(state.result); + state.tag = type.tag; + if (state.anchor !== null && typeof state.anchorMap !== "undefined") { + state.anchorMap[state.anchor] = state.result; + } + break; + } + } + } else if ( + _hasOwnProperty.call(state.typeMap[state.kind || "fallback"], state.tag) + ) { + type = state.typeMap[state.kind || "fallback"][state.tag]; + + if (state.result !== null && type.kind !== state.kind) { + return throwError( + state, + `unacceptable node kind for !<${state.tag}> tag; it should be "${ + type.kind + }", not "${state.kind}"` + ); + } + + if (!type.resolve(state.result)) { + // `state.result` updated in resolver if matched + return throwError( + state, + `cannot resolve a node with !<${state.tag}> explicit tag` + ); + } else { + state.result = type.construct(state.result); + if (state.anchor !== null && typeof state.anchorMap !== "undefined") { + state.anchorMap[state.anchor] = state.result; + } + } + } else { + return throwError(state, `unknown tag !<${state.tag}>`); + } + } + + if (state.listener && state.listener !== null) { + state.listener("close", state); + } + return state.tag !== null || state.anchor !== null || hasContent; +} + +function readDocument(state: LoaderState): void { + const documentStart = state.position; + let position: number, + directiveName: string, + directiveArgs: Any[], + hasDirectives = false, + ch: number; + + state.version = null; + state.checkLineBreaks = state.legacy; + state.tagMap = {}; + state.anchorMap = {}; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + skipSeparationSpace(state, true, -1); + + ch = state.input.charCodeAt(state.position); + + if (state.lineIndent > 0 || ch !== 0x25 /* % */) { + break; + } + + hasDirectives = true; + ch = state.input.charCodeAt(++state.position); + position = state.position; + + while (ch !== 0 && !isWsOrEol(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + directiveName = state.input.slice(position, state.position); + directiveArgs = []; + + if (directiveName.length < 1) { + return throwError( + state, + "directive name must not be less than one character in length" + ); + } + + while (ch !== 0) { + while (isWhiteSpace(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (ch === 0x23 /* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0 && !isEOL(ch)); + break; + } + + if (isEOL(ch)) break; + + position = state.position; + + while (ch !== 0 && !isWsOrEol(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + directiveArgs.push(state.input.slice(position, state.position)); + } + + if (ch !== 0) readLineBreak(state); + + if (_hasOwnProperty.call(directiveHandlers, directiveName)) { + directiveHandlers[directiveName](state, directiveName, ...directiveArgs); + } else { + throwWarning(state, `unknown document directive "${directiveName}"`); + } + } + + skipSeparationSpace(state, true, -1); + + if ( + state.lineIndent === 0 && + state.input.charCodeAt(state.position) === 0x2d /* - */ && + state.input.charCodeAt(state.position + 1) === 0x2d /* - */ && + state.input.charCodeAt(state.position + 2) === 0x2d /* - */ + ) { + state.position += 3; + skipSeparationSpace(state, true, -1); + } else if (hasDirectives) { + return throwError(state, "directives end mark is expected"); + } + + composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); + skipSeparationSpace(state, true, -1); + + if ( + state.checkLineBreaks && + PATTERN_NON_ASCII_LINE_BREAKS.test( + state.input.slice(documentStart, state.position) + ) + ) { + throwWarning(state, "non-ASCII line breaks are interpreted as content"); + } + + state.documents.push(state.result); + + if (state.position === state.lineStart && testDocumentSeparator(state)) { + if (state.input.charCodeAt(state.position) === 0x2e /* . */) { + state.position += 3; + skipSeparationSpace(state, true, -1); + } + return; + } + + if (state.position < state.length - 1) { + return throwError( + state, + "end of the stream or a document separator is expected" + ); + } else { + return; + } +} + +function loadDocuments(input: string, options?: LoaderStateOptions): Any[] { + input = String(input); + options = options || {}; + + if (input.length !== 0) { + // Add tailing `\n` if not exists + if ( + input.charCodeAt(input.length - 1) !== 0x0a /* LF */ && + input.charCodeAt(input.length - 1) !== 0x0d /* CR */ + ) { + input += "\n"; + } + + // Strip BOM + if (input.charCodeAt(0) === 0xfeff) { + input = input.slice(1); + } + } + + const state = new LoaderState(input, options); + + // Use 0 as string terminator. That significantly simplifies bounds check. + state.input += "\0"; + + while (state.input.charCodeAt(state.position) === 0x20 /* Space */) { + state.lineIndent += 1; + state.position += 1; + } + + while (state.position < state.length - 1) { + readDocument(state); + } + + return state.documents; +} + +export type CbFunction = (doc: Any) => void; +function isCbFunction(fn: Any): fn is CbFunction { + return typeof fn === "function"; +} + +export function loadAll( + input: string, + iteratorOrOption?: T, + options?: LoaderStateOptions +): T extends CbFunction ? void : Any[] { + if (!isCbFunction(iteratorOrOption)) { + return loadDocuments(input, iteratorOrOption as LoaderStateOptions) as Any; + } + + const documents = loadDocuments(input, options); + const iterator = iteratorOrOption; + for (let index = 0, length = documents.length; index < length; index++) { + iterator(documents[index]); + } + + return void 0 as Any; +} + +export function load(input: string, options?: LoaderStateOptions): Any { + const documents = loadDocuments(input, options); + + if (documents.length === 0) { + return; + } + if (documents.length === 1) { + return documents[0]; + } + throw new YAMLError( + "expected a single document in the stream, but found more" + ); +} diff --git a/encoding/yaml/loader/loader_state.ts b/encoding/yaml/loader/loader_state.ts new file mode 100644 index 000000000000..3fe8458be378 --- /dev/null +++ b/encoding/yaml/loader/loader_state.ts @@ -0,0 +1,73 @@ +// 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 { 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; + } +} diff --git a/encoding/yaml/parse.ts b/encoding/yaml/parse.ts new file mode 100644 index 000000000000..d2687094b7a3 --- /dev/null +++ b/encoding/yaml/parse.ts @@ -0,0 +1,21 @@ +// 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. +// 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; + +export function parse(content: string, options?: ParseOptions): unknown { + return load(content, options); +} + +export function parseAll( + content: string, + iterator?: CbFunction, + options?: ParseOptions +): unknown { + return loadAll(content, iterator, options); +} diff --git a/encoding/yaml/parse_test.ts b/encoding/yaml/parse_test.ts new file mode 100644 index 000000000000..c52767111cab --- /dev/null +++ b/encoding/yaml/parse_test.ts @@ -0,0 +1,25 @@ +// 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. +// 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); + } +}); diff --git a/encoding/yaml/schema/core.ts b/encoding/yaml/schema/core.ts new file mode 100644 index 000000000000..910e979d8e64 --- /dev/null +++ b/encoding/yaml/schema/core.ts @@ -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 { 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] +}); diff --git a/encoding/yaml/schema/default.ts b/encoding/yaml/schema/default.ts new file mode 100644 index 000000000000..2618d198bfb0 --- /dev/null +++ b/encoding/yaml/schema/default.ts @@ -0,0 +1,15 @@ +// 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 { 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] +}); diff --git a/encoding/yaml/schema/failsafe.ts b/encoding/yaml/schema/failsafe.ts new file mode 100644 index 000000000000..885d058f37a8 --- /dev/null +++ b/encoding/yaml/schema/failsafe.ts @@ -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 { 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] +}); diff --git a/encoding/yaml/schema/json.ts b/encoding/yaml/schema/json.ts new file mode 100644 index 000000000000..129b916d3d84 --- /dev/null +++ b/encoding/yaml/schema/json.ts @@ -0,0 +1,14 @@ +// 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 { 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] +}); diff --git a/encoding/yaml/schema/mod.ts b/encoding/yaml/schema/mod.ts new file mode 100644 index 000000000000..27967d29cefc --- /dev/null +++ b/encoding/yaml/schema/mod.ts @@ -0,0 +1,8 @@ +// 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. + +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"; diff --git a/encoding/yaml/stringify.ts b/encoding/yaml/stringify.ts new file mode 100644 index 000000000000..9344c3341e5d --- /dev/null +++ b/encoding/yaml/stringify.ts @@ -0,0 +1,13 @@ +// 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. +// 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; + +export function stringify(obj: object, options?: DumpOptions): string { + return dump(obj, options); +} diff --git a/encoding/yaml/stringify_test.ts b/encoding/yaml/stringify_test.ts new file mode 100644 index 000000000000..2f03399e744d --- /dev/null +++ b/encoding/yaml/stringify_test.ts @@ -0,0 +1,42 @@ +// 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. +// 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); + } +}); diff --git a/encoding/yaml/type/binary.ts b/encoding/yaml/type/binary.ts new file mode 100644 index 000000000000..863b4f5f0384 --- /dev/null +++ b/encoding/yaml/type/binary.ts @@ -0,0 +1,138 @@ +// 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"; +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 +}); diff --git a/encoding/yaml/type/bool.ts b/encoding/yaml/type/bool.ts new file mode 100644 index 000000000000..e8f2d47a6a2a --- /dev/null +++ b/encoding/yaml/type/bool.ts @@ -0,0 +1,38 @@ +// 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"; +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 +}); diff --git a/encoding/yaml/type/float.ts b/encoding/yaml/type/float.ts new file mode 100644 index 000000000000..f5be2e1b9339 --- /dev/null +++ b/encoding/yaml/type/float.ts @@ -0,0 +1,130 @@ +// 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 { 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 { + let res; + + 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"; + } + + 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 +}); diff --git a/encoding/yaml/type/int.ts b/encoding/yaml/type/int.ts new file mode 100644 index 000000000000..86032d606f76 --- /dev/null +++ b/encoding/yaml/type/int.ts @@ -0,0 +1,193 @@ +// 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"; +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"] + } +}); diff --git a/encoding/yaml/type/map.ts b/encoding/yaml/type/map.ts new file mode 100644 index 000000000000..fd4258345044 --- /dev/null +++ b/encoding/yaml/type/map.ts @@ -0,0 +1,13 @@ +// 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"; +import { Any } from "../utils.ts"; + +export const map = new Type("tag:yaml.org,2002:map", { + construct(data): Any { + return data !== null ? data : {}; + }, + kind: "mapping" +}); diff --git a/encoding/yaml/type/merge.ts b/encoding/yaml/type/merge.ts new file mode 100644 index 000000000000..7da8093a29d1 --- /dev/null +++ b/encoding/yaml/type/merge.ts @@ -0,0 +1,14 @@ +// 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"; + +function resolveYamlMerge(data: string): boolean { + return data === "<<" || data === null; +} + +export const merge = new Type("tag:yaml.org,2002:merge", { + kind: "scalar", + resolve: resolveYamlMerge +}); diff --git a/encoding/yaml/type/mod.ts b/encoding/yaml/type/mod.ts new file mode 100644 index 000000000000..1e4a33611898 --- /dev/null +++ b/encoding/yaml/type/mod.ts @@ -0,0 +1,17 @@ +// 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. + +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"; diff --git a/encoding/yaml/type/nil.ts b/encoding/yaml/type/nil.ts new file mode 100644 index 000000000000..60c74c0c49be --- /dev/null +++ b/encoding/yaml/type/nil.ts @@ -0,0 +1,44 @@ +// 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"; + +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 +}); diff --git a/encoding/yaml/type/omap.ts b/encoding/yaml/type/omap.ts new file mode 100644 index 000000000000..7f66694b8189 --- /dev/null +++ b/encoding/yaml/type/omap.ts @@ -0,0 +1,45 @@ +// 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"; +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 +}); diff --git a/encoding/yaml/type/pairs.ts b/encoding/yaml/type/pairs.ts new file mode 100644 index 000000000000..66838c897d44 --- /dev/null +++ b/encoding/yaml/type/pairs.ts @@ -0,0 +1,48 @@ +// 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"; +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 +}); diff --git a/encoding/yaml/type/seq.ts b/encoding/yaml/type/seq.ts new file mode 100644 index 000000000000..89b7124c2b41 --- /dev/null +++ b/encoding/yaml/type/seq.ts @@ -0,0 +1,13 @@ +// 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"; +import { Any } from "../utils.ts"; + +export const seq = new Type("tag:yaml.org,2002:seq", { + construct(data): Any { + return data !== null ? data : []; + }, + kind: "sequence" +}); diff --git a/encoding/yaml/type/set.ts b/encoding/yaml/type/set.ts new file mode 100644 index 000000000000..3a6cab7e0e09 --- /dev/null +++ b/encoding/yaml/type/set.ts @@ -0,0 +1,30 @@ +// 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"; +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 +}); diff --git a/encoding/yaml/type/str.ts b/encoding/yaml/type/str.ts new file mode 100644 index 000000000000..8c24e9398929 --- /dev/null +++ b/encoding/yaml/type/str.ts @@ -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" +}); diff --git a/encoding/yaml/type/timestamp.ts b/encoding/yaml/type/timestamp.ts new file mode 100644 index 000000000000..68b10fbb0b43 --- /dev/null +++ b/encoding/yaml/type/timestamp.ts @@ -0,0 +1,95 @@ +// 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"; + +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 +}); diff --git a/encoding/yaml/utils.ts b/encoding/yaml/utils.ts new file mode 100644 index 000000000000..d91338f93c47 --- /dev/null +++ b/encoding/yaml/utils.ts @@ -0,0 +1,84 @@ +// 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. +// 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: Any): 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(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 { + [P: string]: T; +} diff --git a/encoding/yaml_test.ts b/encoding/yaml_test.ts new file mode 100644 index 000000000000..40de6a94dc35 --- /dev/null +++ b/encoding/yaml_test.ts @@ -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";