Skip to content

Commit

Permalink
move tagObj.createNode into nodeClass.from static method
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacs committed Apr 20, 2023
1 parent ffdd2fb commit b67ffa8
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 69 deletions.
2 changes: 2 additions & 0 deletions src/doc/createNode.ts
Expand Up @@ -99,6 +99,8 @@ export function createNode(

const node = tagObj?.createNode
? tagObj.createNode(ctx.schema, value, ctx)
: typeof tagObj?.nodeClass?.from === 'function'
? tagObj.nodeClass.from(ctx.schema, value, ctx)
: new Scalar(value)
if (tagName) node.tag = tagName
else if (!tagObj.default) node.tag = tagObj.tag
Expand Down
27 changes: 26 additions & 1 deletion src/nodes/YAMLMap.ts
Expand Up @@ -2,11 +2,12 @@ import type { BlockMap, FlowCollection } from '../parse/cst.js'
import type { Schema } from '../schema/Schema.js'
import type { StringifyContext } from '../stringify/stringify.js'
import { stringifyCollection } from '../stringify/stringifyCollection.js'
import { CreateNodeContext } from '../util.js'
import { addPairToJSMap } from './addPairToJSMap.js'
import { Collection } from './Collection.js'
import { isPair, isScalar, MAP } from './identity.js'
import type { ParsedNode, Range } from './Node.js'
import { Pair } from './Pair.js'
import { createPair, Pair } from './Pair.js'
import { isScalarValue, Scalar } from './Scalar.js'
import type { ToJSContext } from './toJS.js'

Expand Down Expand Up @@ -51,6 +52,30 @@ export class YAMLMap<K = unknown, V = unknown> extends Collection {
super(MAP, schema)
}

/**
* A generic collection parsing method that can be extended
* to other node classes that inherit from YAMLMap
*/
static from(schema: Schema, obj: unknown, ctx: CreateNodeContext) {
const { keepUndefined, replacer } = ctx
const map = new this(schema)
const add = (key: unknown, value: unknown) => {
if (typeof replacer === 'function') value = replacer.call(obj, key, value)
else if (Array.isArray(replacer) && !replacer.includes(key)) return
if (value !== undefined || keepUndefined)
map.items.push(createPair(key, value, ctx))
}
if (obj instanceof Map) {
for (const [key, value] of obj) add(key, value)
} else if (obj && typeof obj === 'object') {
for (const key of Object.keys(obj)) add(key, (obj as any)[key])
}
if (typeof schema.sortMapEntries === 'function') {
map.items.sort(schema.sortMapEntries)
}
return map
}

/**
* Adds a value to the collection.
*
Expand Down
17 changes: 17 additions & 0 deletions src/nodes/YAMLSeq.ts
Expand Up @@ -2,6 +2,7 @@ import type { BlockSequence, FlowCollection } from '../parse/cst.js'
import type { Schema } from '../schema/Schema.js'
import type { StringifyContext } from '../stringify/stringify.js'
import { stringifyCollection } from '../stringify/stringifyCollection.js'
import { createNode, CreateNodeContext } from '../util.js'
import { Collection } from './Collection.js'
import { isScalar, SEQ } from './identity.js'
import type { ParsedNode, Range } from './Node.js'
Expand Down Expand Up @@ -116,6 +117,22 @@ export class YAMLSeq<T = unknown> extends Collection {
onComment
})
}

static from(schema: Schema, obj: unknown, ctx: CreateNodeContext) {
const { replacer } = ctx
const seq = new YAMLSeq(schema)
if (obj && Symbol.iterator in Object(obj)) {
let i = 0
for (let it of obj as Iterable<unknown>) {
if (typeof replacer === 'function') {
const key = obj instanceof Set ? it : String(i++)
it = replacer.call(obj, key, it)
}
seq.items.push(createNode(it, undefined, ctx))
}
}
return seq
}
}

function asItemIndex(key: unknown): number | null {
Expand Down
24 changes: 0 additions & 24 deletions src/schema/common/map.ts
@@ -1,33 +1,9 @@
import type { CreateNodeContext } from '../../doc/createNode.js'
import { isMap } from '../../nodes/identity.js'
import { createPair } from '../../nodes/Pair.js'
import { YAMLMap } from '../../nodes/YAMLMap.js'
import type { CollectionTag } from '../types.js'
import type { Schema } from '../Schema.js'

function createMap(schema: Schema, obj: unknown, ctx: CreateNodeContext) {
const { keepUndefined, replacer } = ctx
const map = new YAMLMap(schema)
const add = (key: unknown, value: unknown) => {
if (typeof replacer === 'function') value = replacer.call(obj, key, value)
else if (Array.isArray(replacer) && !replacer.includes(key)) return
if (value !== undefined || keepUndefined)
map.items.push(createPair(key, value, ctx))
}
if (obj instanceof Map) {
for (const [key, value] of obj) add(key, value)
} else if (obj && typeof obj === 'object') {
for (const key of Object.keys(obj)) add(key, (obj as any)[key])
}
if (typeof schema.sortMapEntries === 'function') {
map.items.sort(schema.sortMapEntries)
}
return map
}

export const map: CollectionTag = {
collection: 'map',
createNode: createMap,
default: true,
nodeClass: YAMLMap,
tag: 'tag:yaml.org,2002:map',
Expand Down
19 changes: 0 additions & 19 deletions src/schema/common/seq.ts
@@ -1,28 +1,9 @@
import { CreateNodeContext, createNode } from '../../doc/createNode.js'
import { isSeq } from '../../nodes/identity.js'
import { YAMLSeq } from '../../nodes/YAMLSeq.js'
import type { Schema } from '../Schema.js'
import type { CollectionTag } from '../types.js'

function createSeq(schema: Schema, obj: unknown, ctx: CreateNodeContext) {
const { replacer } = ctx
const seq = new YAMLSeq(schema)
if (obj && Symbol.iterator in Object(obj)) {
let i = 0
for (let it of obj as Iterable<unknown>) {
if (typeof replacer === 'function') {
const key = obj instanceof Set ? it : String(i++)
it = replacer.call(obj, key, it)
}
seq.items.push(createNode(it, undefined, ctx))
}
}
return seq
}

export const seq: CollectionTag = {
collection: 'seq',
createNode: createSeq,
default: true,
nodeClass: YAMLSeq,
tag: 'tag:yaml.org,2002:seq',
Expand Down
8 changes: 7 additions & 1 deletion src/schema/types.ts
Expand Up @@ -92,8 +92,14 @@ export interface CollectionTag extends TagBase {
/**
* The `Node` child class that implements this tag.
* If set, used to select this tag when stringifying.
*
* If the class provides a static `from` method, then that
* will be used if the tag object doesn't have a `createNode` method.
*/
nodeClass?: new () => Node
nodeClass?: {
new (): Node
from?: (schema: Schema, obj: unknown, ctx: CreateNodeContext) => Node
}

/**
* Turns a value into an AST node.
Expand Down
16 changes: 9 additions & 7 deletions src/schema/yaml-1.1/omap.ts
Expand Up @@ -2,6 +2,8 @@ import { isPair, isScalar } from '../../nodes/identity.js'
import { toJS, ToJSContext } from '../../nodes/toJS.js'
import { YAMLMap } from '../../nodes/YAMLMap.js'
import { YAMLSeq } from '../../nodes/YAMLSeq.js'
import { CreateNodeContext } from '../../util.js'
import type { Schema } from '../Schema.js'
import { CollectionTag } from '../types.js'
import { createPairs, resolvePairs } from './pairs.js'

Expand Down Expand Up @@ -41,6 +43,13 @@ export class YAMLOMap extends YAMLSeq {
}
return map as unknown as unknown[]
}

static from(schema: Schema, iterable: unknown, ctx: CreateNodeContext) {
const pairs = createPairs(schema, iterable, ctx)
const omap = new this()
omap.items = pairs.items
return omap
}
}

export const omap: CollectionTag = {
Expand All @@ -63,12 +72,5 @@ export const omap: CollectionTag = {
}
}
return Object.assign(new YAMLOMap(), pairs)
},

createNode(schema, iterable, ctx) {
const pairs = createPairs(schema, iterable, ctx)
const omap = new YAMLOMap()
omap.items = pairs.items
return omap
}
}
35 changes: 18 additions & 17 deletions src/schema/yaml-1.1/set.ts
@@ -1,10 +1,11 @@
import type { Schema } from '../../schema/Schema.js'
import { isMap, isPair, isScalar } from '../../nodes/identity.js'
import { createPair, Pair } from '../../nodes/Pair.js'
import { Pair } from '../../nodes/Pair.js'
import { Scalar } from '../../nodes/Scalar.js'
import { ToJSContext } from '../../nodes/toJS.js'
import { YAMLMap, findPair } from '../../nodes/YAMLMap.js'
import { findPair, YAMLMap } from '../../nodes/YAMLMap.js'
import type { Schema } from '../../schema/Schema.js'
import type { StringifyContext } from '../../stringify/stringify.js'
import { CreateNodeContext, createPair } from '../../util.js'
import type { CollectionTag } from '../types.js'

export class YAMLSet<T = unknown> extends YAMLMap<T, Scalar<null> | null> {
Expand Down Expand Up @@ -84,6 +85,20 @@ export class YAMLSet<T = unknown> extends YAMLMap<T, Scalar<null> | null> {
)
else throw new Error('Set items must all have null values')
}

static from(schema: Schema, iterable: unknown, ctx: CreateNodeContext) {
const { replacer } = ctx
const set = new this(schema)
if (iterable && Symbol.iterator in Object(iterable))
for (let value of iterable as Iterable<unknown>) {
if (typeof replacer === 'function')
value = replacer.call(iterable, value, value)
set.items.push(
createPair(value, null, ctx) as Pair<unknown, Scalar<null>>
)
}
return set
}
}

export const set: CollectionTag = {
Expand All @@ -99,19 +114,5 @@ export const set: CollectionTag = {
else onError('Set items must all have null values')
} else onError('Expected a mapping for this tag')
return map
},

createNode(schema, iterable, ctx) {
const { replacer } = ctx
const set = new YAMLSet(schema)
if (iterable && Symbol.iterator in Object(iterable))
for (let value of iterable as Iterable<unknown>) {
if (typeof replacer === 'function')
value = replacer.call(iterable, value, value)
set.items.push(
createPair(value, null, ctx) as Pair<unknown, Scalar<null>>
)
}
return set
}
}

0 comments on commit b67ffa8

Please sign in to comment.