Skip to content

Commit

Permalink
✨ Add yaml
Browse files Browse the repository at this point in the history
  • Loading branch information
Lilian Saget-Lethias authored and lsagetlethias committed Jul 3, 2019
1 parent 85db520 commit 0e52744
Show file tree
Hide file tree
Showing 46 changed files with 4,359 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ deno.d.ts
node_modules
package.json
package-lock.json
yarn.lock
.vscode
1 change: 1 addition & 0 deletions test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import "./util/test.ts";
import "./ws/test.ts";
import "./encoding/test.ts";
import "./os/test.ts";
import "./yaml/test.ts";

import { xrun } from "./prettier/util.ts";
import { red, green } from "./colors/mod.ts";
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "tsconfig.test.json",
"extends": "./tsconfig.test.json",
"compilerOptions": {
"allowJs": true,
"baseUrl": ".",
Expand Down
72 changes: 72 additions & 0 deletions yaml/Mark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
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) {
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) {
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;
}
}
16 changes: 16 additions & 0 deletions yaml/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# deno-yaml
YAML parser / dumper for Deno

Heavily inspired from [js-yaml]

# Example
See `./example` folder and [js-yaml] repository.

# 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
95 changes: 95 additions & 0 deletions yaml/Schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { YAMLError } from "./error/YAMLError.ts";
import { KindType, Type } from "./Type.ts";
import { ArrayObject } 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) => !exclude.includes(index));
}

export type TypeMap = { [k in KindType | "fallback"]: ArrayObject<Type> };
function compileMap(...typesList: Type[][]) {
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(
"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() {}
}

export interface SchemaDefinition {
implicit?: any[];
explicit?: Type[];
include?: Schema[];
}
6 changes: 6 additions & 0 deletions yaml/State.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SchemaDefinition } from "./Schema.ts";
import { DEFAULT_FULL_SCHEMA } from "./schema/mod.ts";

export abstract class State {
constructor(public schema: SchemaDefinition = DEFAULT_FULL_SCHEMA) {}
}
50 changes: 50 additions & 0 deletions yaml/Type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ArrayObject } 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 = () => true;
const DEFAULT_CONSTRUCT = (data: any) => data;

interface TypeOptions {
kind: KindType;
resolve?: (data: any) => boolean;
construct?: (data: string) => any;
instanceOf?: any;
predicate?: (data: object) => boolean;
represent?: RepresentFn | ArrayObject<RepresentFn>;
defaultStyle?: StyleVariant;
styleAliases?: { [x: string]: any };
}

function checkTagFormat(tag: string): string {
return tag;
}

export class Type {
public tag: string;
public kind: KindType | null = null;
public instanceOf: any;
public predicate?: (data: object) => boolean;
public represent?: RepresentFn | ArrayObject<RepresentFn>;
public defaultStyle?: StyleVariant;
public styleAliases?: { [x: string]: any };
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 = () => true;
public construct: (data?: any) => any = data => data;
}
3 changes: 3 additions & 0 deletions yaml/devDeps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as asserts from "../testing/asserts.ts";
import * as testing from "../testing/mod.ts";
export { testing, asserts };
111 changes: 111 additions & 0 deletions yaml/dumper/DumperState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { Schema, SchemaDefinition } from "../Schema.ts";
import { State } from "../State.ts";
import { StyleVariant, Type } from "../Type.ts";
import { ArrayObject } from "../utils.ts";

const _hasOwnProperty = Object.prototype.hasOwnProperty;

function compileStyleMap(
schema: Schema,
map?: ArrayObject<StyleVariant> | null
): ArrayObject<StyleVariant> {
if (typeof map === "undefined" || map === null) return {};

let type: Type;
const result: ArrayObject<StyleVariant> = {};
const keys = Object.keys(map);
let tag: string, style: StyleVariant;
for (let index = 0, length = keys.length; index < length; index += 1) {
tag = keys[index];
style = String(map[tag]) as StyleVariant;
if (tag.slice(0, 2) === "!!") {
tag = `tag:yaml.org,2002:${tag.slice(2)}`;
}
type = schema.compiledTypeMap.fallback[tag];

if (type && _hasOwnProperty.call(type.styleAliases, style)) {
style = type.styleAliases[style];
}

result[tag] = style;
}

return result;
}

export interface DumperStateOptions {
/** indentation width to use (in spaces). */
indent?: number;
/** when true, will not add an indentation level to array elements */
noArrayIndent?: boolean;
/** do not throw on invalid types (like function in the safe schema) and skip pairs and single values with such types. */
skipInvalid?: boolean;
/** specifies level of nesting, when to switch from block to flow style for collections. -1 means block style everwhere */
flowLevel?: number;
/** Each tag may have own set of styles. - "tag" => "style" map. */
styles?: ArrayObject<StyleVariant> | null;
/** specifies a schema to use. */
schema?: SchemaDefinition;
/** if true, sort keys when dumping YAML. If a function, use the function to sort the keys. (default: false) */
sortKeys?: boolean | ((a: any, b: any) => number);
/** set max line width. (default: 80) */
lineWidth?: number;
/** if true, don't convert duplicate objects into references (default: false) */
noRefs?: boolean;
/** if true don't try to be compatible with older yaml versions. Currently: don't quote "yes", "no" and so on, as required for YAML 1.1 (default: false) */
noCompatMode?: boolean;
/**
* if true flow sequences will be condensed, omitting the space between `key: value` or `a, b`. Eg. `'[a,b]'` or `{a:{b:c}}`.
* Can be useful when using yaml for pretty URL query params as spaces are %-encoded. (default: false).
*/
condenseFlow?: boolean;
}

export class DumperState extends State {
public indent: number;
public noArrayIndent: boolean;
public skipInvalid: boolean;
public flowLevel: number;
public sortKeys: boolean | ((a: any, b: any) => number);
public lineWidth: number;
public noRefs: boolean;
public noCompatMode: boolean;
public condenseFlow: boolean;
public implicitTypes: Type[];
public explicitTypes: Type[];
public tag: string | null = null;
public result: string = "";
public duplicates = [];
public usedDuplicates = null;
public styleMap: ArrayObject<StyleVariant>;
public dump: any;

constructor({
schema,
indent = 2,
noArrayIndent = false,
skipInvalid = false,
flowLevel = -1,
styles = null,
sortKeys = false,
lineWidth = 80,
noRefs = false,
noCompatMode = false,
condenseFlow = false
}: DumperStateOptions) {
super(schema);
this.indent = Math.max(1, indent);
this.noArrayIndent = noArrayIndent;
this.skipInvalid = skipInvalid;
this.flowLevel = flowLevel;
this.styleMap = compileStyleMap(this.schema as Schema, styles);
this.sortKeys = sortKeys;
this.lineWidth = lineWidth;
this.noRefs = noRefs;
this.noCompatMode = noCompatMode;
this.condenseFlow = condenseFlow;

this.implicitTypes = (this.schema as Schema).compiledImplicit;
this.explicitTypes = (this.schema as Schema).compiledExplicit;
}
}
Loading

0 comments on commit 0e52744

Please sign in to comment.