From ce01fc1bdc9583fa308367871b2e3a5456a8f372 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Wed, 15 Mar 2023 14:03:26 +0200 Subject: [PATCH 1/2] refactor: Split nodes/identity.ts from nodes/Node.ts --- src/compose/compose-collection.ts | 3 +- src/compose/compose-scalar.ts | 2 +- src/compose/composer.ts | 3 +- src/compose/resolve-flow-collection.ts | 2 +- src/compose/util-map-includes.ts | 7 +-- src/doc/Document.ts | 13 ++--- src/doc/anchors.ts | 3 +- src/doc/createNode.ts | 3 +- src/doc/directives.ts | 2 +- src/index.ts | 8 ++- src/nodes/Alias.ts | 11 +---- src/nodes/Collection.ts | 4 +- src/nodes/Node.ts | 64 +----------------------- src/nodes/Pair.ts | 2 +- src/nodes/Scalar.ts | 3 +- src/nodes/YAMLMap.ts | 3 +- src/nodes/YAMLSeq.ts | 3 +- src/nodes/addPairToJSMap.ts | 2 +- src/nodes/identity.ts | 68 ++++++++++++++++++++++++++ src/nodes/toJS.ts | 7 +-- src/schema/Schema.ts | 2 +- src/schema/common/map.ts | 2 +- src/schema/common/seq.ts | 2 +- src/schema/yaml-1.1/omap.ts | 4 +- src/schema/yaml-1.1/pairs.ts | 3 +- src/schema/yaml-1.1/set.ts | 2 +- src/stringify/stringify.ts | 6 +-- src/stringify/stringifyCollection.ts | 2 +- src/stringify/stringifyDocument.ts | 7 +-- src/stringify/stringifyPair.ts | 2 +- src/test-events.ts | 7 ++- src/visit.ts | 6 +-- 32 files changed, 128 insertions(+), 130 deletions(-) create mode 100644 src/nodes/identity.ts diff --git a/src/compose/compose-collection.ts b/src/compose/compose-collection.ts index cb6606b8..cb6749ad 100644 --- a/src/compose/compose-collection.ts +++ b/src/compose/compose-collection.ts @@ -1,4 +1,5 @@ -import { isMap, isNode, ParsedNode } from '../nodes/Node.js' +import { isMap, isNode } from '../nodes/identity.js' +import type { ParsedNode } from '../nodes/Node.js' import { Scalar } from '../nodes/Scalar.js' import type { YAMLMap } from '../nodes/YAMLMap.js' import type { YAMLSeq } from '../nodes/YAMLSeq.js' diff --git a/src/compose/compose-scalar.ts b/src/compose/compose-scalar.ts index bc672f21..726f777e 100644 --- a/src/compose/compose-scalar.ts +++ b/src/compose/compose-scalar.ts @@ -1,4 +1,4 @@ -import { isScalar, SCALAR } from '../nodes/Node.js' +import { isScalar, SCALAR } from '../nodes/identity.js' import { Scalar } from '../nodes/Scalar.js' import type { BlockScalar, FlowScalar, SourceToken } from '../parse/cst.js' import type { Schema } from '../schema/Schema.js' diff --git a/src/compose/composer.ts b/src/compose/composer.ts index b7bb0ffa..4def6a00 100644 --- a/src/compose/composer.ts +++ b/src/compose/composer.ts @@ -1,7 +1,8 @@ import { Directives } from '../doc/directives.js' import { Document } from '../doc/Document.js' import { ErrorCode, YAMLParseError, YAMLWarning } from '../errors.js' -import { isCollection, isPair, ParsedNode, Range } from '../nodes/Node.js' +import { isCollection, isPair } from '../nodes/identity.js' +import type { ParsedNode, Range } from '../nodes/Node.js' import type { DocumentOptions, ParseOptions, diff --git a/src/compose/resolve-flow-collection.ts b/src/compose/resolve-flow-collection.ts index 9a894b45..64d280b7 100644 --- a/src/compose/resolve-flow-collection.ts +++ b/src/compose/resolve-flow-collection.ts @@ -1,4 +1,4 @@ -import { isPair } from '../nodes/Node.js' +import { isPair } from '../nodes/identity.js' import { Pair } from '../nodes/Pair.js' import { YAMLMap } from '../nodes/YAMLMap.js' import { YAMLSeq } from '../nodes/YAMLSeq.js' diff --git a/src/compose/util-map-includes.ts b/src/compose/util-map-includes.ts index f41ec7e2..e1e36a0f 100644 --- a/src/compose/util-map-includes.ts +++ b/src/compose/util-map-includes.ts @@ -1,6 +1,7 @@ -import { isScalar, ParsedNode } from '../nodes/Node' -import { Pair } from '../nodes/Pair' -import { ComposeContext } from './compose-node' +import { isScalar } from '../nodes/identity.js' +import type { ParsedNode } from '../nodes/Node.js' +import type { Pair } from '../nodes/Pair.js' +import type { ComposeContext } from './compose-node.js' export function mapIncludes( ctx: ComposeContext, diff --git a/src/doc/Document.ts b/src/doc/Document.ts index a5f62591..5f0b5a74 100644 --- a/src/doc/Document.ts +++ b/src/doc/Document.ts @@ -6,12 +6,9 @@ import { isCollection, isNode, isScalar, - Node, - NodeType, - NODE_TYPE, - ParsedNode, - Range -} from '../nodes/Node.js' + NODE_TYPE +} from '../nodes/identity.js' +import type { Node, NodeType, ParsedNode, Range } from '../nodes/Node.js' import { Pair } from '../nodes/Pair.js' import type { Scalar } from '../nodes/Scalar.js' import { toJS, ToJSContext } from '../nodes/toJS.js' @@ -26,7 +23,6 @@ import type { ToStringOptions } from '../options.js' import { Schema } from '../schema/Schema.js' -import { stringify } from '../stringify/stringify.js' import { stringifyDocument } from '../stringify/stringifyDocument.js' import { anchorNames, createNodeAnchors, findNewAnchor } from './anchors.js' import { applyReviver } from './applyReviver.js' @@ -434,8 +430,7 @@ export class Document< keep: !json, mapAsMap: mapAsMap === true, mapKeyWarned: false, - maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100, - stringify + maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100 } const res = toJS(this.contents, jsonArg ?? '', ctx) if (typeof onAnchor === 'function') diff --git a/src/doc/anchors.ts b/src/doc/anchors.ts index 07cff9ec..7899fbe5 100644 --- a/src/doc/anchors.ts +++ b/src/doc/anchors.ts @@ -1,4 +1,5 @@ -import { isCollection, isScalar, Node } from '../nodes/Node.js' +import { isCollection, isScalar } from '../nodes/identity.js' +import type { Node } from '../nodes/Node.js' import type { Scalar } from '../nodes/Scalar.js' import type { YAMLMap } from '../nodes/YAMLMap.js' import type { YAMLSeq } from '../nodes/YAMLSeq.js' diff --git a/src/doc/createNode.ts b/src/doc/createNode.ts index 8f4c3ec2..d6e72417 100644 --- a/src/doc/createNode.ts +++ b/src/doc/createNode.ts @@ -1,5 +1,6 @@ import { Alias } from '../nodes/Alias.js' -import { isDocument, isNode, isPair, MAP, Node, SEQ } from '../nodes/Node.js' +import { isDocument, isNode, isPair, MAP, SEQ } from '../nodes/identity.js' +import type { Node } from '../nodes/Node.js' import { Scalar } from '../nodes/Scalar.js' import type { YAMLMap } from '../nodes/YAMLMap.js' import type { Schema } from '../schema/Schema.js' diff --git a/src/doc/directives.ts b/src/doc/directives.ts index 210d22b2..4978c07f 100644 --- a/src/doc/directives.ts +++ b/src/doc/directives.ts @@ -1,4 +1,4 @@ -import { isNode } from '../nodes/Node.js' +import { isNode } from '../nodes/identity.js' import { visit } from '../visit.js' import type { Document } from './Document.js' diff --git a/src/index.ts b/src/index.ts index 76555122..1b5ea23d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,11 +14,9 @@ export { isNode, isPair, isScalar, - isSeq, - Node, - ParsedNode, - Range -} from './nodes/Node.js' + isSeq +} from './nodes/identity.js' +export { Node, ParsedNode, Range } from './nodes/Node.js' export { Pair } from './nodes/Pair.js' export { Scalar } from './nodes/Scalar.js' export { YAMLMap } from './nodes/YAMLMap.js' diff --git a/src/nodes/Alias.ts b/src/nodes/Alias.ts index 4c242cf3..213a667a 100644 --- a/src/nodes/Alias.ts +++ b/src/nodes/Alias.ts @@ -3,15 +3,8 @@ import type { Document } from '../doc/Document.js' import type { FlowScalar } from '../parse/cst.js' import type { StringifyContext } from '../stringify/stringify.js' import { visit } from '../visit.js' -import { - ALIAS, - isAlias, - isCollection, - isPair, - Node, - NodeBase, - Range -} from './Node.js' +import { ALIAS, isAlias, isCollection, isPair } from './identity.js' +import { Node, NodeBase, Range } from './Node.js' import type { Scalar } from './Scalar' import type { ToJSContext } from './toJS.js' import type { YAMLMap } from './YAMLMap.js' diff --git a/src/nodes/Collection.ts b/src/nodes/Collection.ts index 27508b3f..40ab0d9a 100644 --- a/src/nodes/Collection.ts +++ b/src/nodes/Collection.ts @@ -5,9 +5,9 @@ import { isNode, isPair, isScalar, - NodeBase, NODE_TYPE -} from './Node.js' +} from './identity.js' +import { NodeBase } from './Node.js' export function collectionFromPath( schema: Schema, diff --git a/src/nodes/Node.ts b/src/nodes/Node.ts index 6e39022e..355215de 100644 --- a/src/nodes/Node.ts +++ b/src/nodes/Node.ts @@ -1,8 +1,7 @@ -import type { Document } from '../doc/Document.js' import { Token } from '../parse/cst.js' import type { StringifyContext } from '../stringify/stringify.js' import type { Alias } from './Alias.js' -import type { Pair } from './Pair.js' +import { NODE_TYPE } from './identity.js' import type { Scalar } from './Scalar.js' import type { YAMLMap } from './YAMLMap.js' import type { YAMLSeq } from './YAMLSeq.js' @@ -40,67 +39,6 @@ export type ParsedNode = export type Range = [number, number, number] -export const ALIAS = Symbol.for('yaml.alias') -export const DOC = Symbol.for('yaml.document') -export const MAP = Symbol.for('yaml.map') -export const PAIR = Symbol.for('yaml.pair') -export const SCALAR = Symbol.for('yaml.scalar') -export const SEQ = Symbol.for('yaml.seq') -export const NODE_TYPE = Symbol.for('yaml.node.type') - -export const isAlias = (node: any): node is Alias => - !!node && typeof node === 'object' && node[NODE_TYPE] === ALIAS - -export const isDocument = ( - node: any -): node is Document => - !!node && typeof node === 'object' && node[NODE_TYPE] === DOC - -export const isMap = ( - node: any -): node is YAMLMap => - !!node && typeof node === 'object' && node[NODE_TYPE] === MAP - -export const isPair = ( - node: any -): node is Pair => - !!node && typeof node === 'object' && node[NODE_TYPE] === PAIR - -export const isScalar = (node: any): node is Scalar => - !!node && typeof node === 'object' && node[NODE_TYPE] === SCALAR - -export const isSeq = (node: any): node is YAMLSeq => - !!node && typeof node === 'object' && node[NODE_TYPE] === SEQ - -export function isCollection( - node: any -): node is YAMLMap | YAMLSeq { - if (node && typeof node === 'object') - switch (node[NODE_TYPE]) { - case MAP: - case SEQ: - return true - } - return false -} - -export function isNode(node: any): node is Node { - if (node && typeof node === 'object') - switch (node[NODE_TYPE]) { - case ALIAS: - case MAP: - case SCALAR: - case SEQ: - return true - } - return false -} - -export const hasAnchor = ( - node: unknown -): node is Scalar | YAMLMap | YAMLSeq => - (isScalar(node) || isCollection(node)) && !!node.anchor - export abstract class NodeBase { declare readonly [NODE_TYPE]: symbol diff --git a/src/nodes/Pair.ts b/src/nodes/Pair.ts index 9c760432..370fc2c3 100644 --- a/src/nodes/Pair.ts +++ b/src/nodes/Pair.ts @@ -4,7 +4,7 @@ import type { Schema } from '../schema/Schema.js' import type { StringifyContext } from '../stringify/stringify.js' import { stringifyPair } from '../stringify/stringifyPair.js' import { addPairToJSMap } from './addPairToJSMap.js' -import { isNode, NODE_TYPE, PAIR } from './Node.js' +import { isNode, NODE_TYPE, PAIR } from './identity.js' import type { ToJSContext } from './toJS.js' export function createPair( diff --git a/src/nodes/Scalar.ts b/src/nodes/Scalar.ts index ac17e017..59ea16a9 100644 --- a/src/nodes/Scalar.ts +++ b/src/nodes/Scalar.ts @@ -1,5 +1,6 @@ import type { BlockScalar, FlowScalar } from '../parse/cst.js' -import { NodeBase, Range, SCALAR } from './Node.js' +import { SCALAR } from './identity.js' +import { NodeBase, Range } from './Node.js' import { toJS, ToJSContext } from './toJS.js' export const isScalarValue = (value: unknown) => diff --git a/src/nodes/YAMLMap.ts b/src/nodes/YAMLMap.ts index 1198ba6c..df2fb3f4 100644 --- a/src/nodes/YAMLMap.ts +++ b/src/nodes/YAMLMap.ts @@ -4,7 +4,8 @@ import type { StringifyContext } from '../stringify/stringify.js' import { stringifyCollection } from '../stringify/stringifyCollection.js' import { addPairToJSMap } from './addPairToJSMap.js' import { Collection } from './Collection.js' -import { isPair, isScalar, MAP, ParsedNode, Range } from './Node.js' +import { isPair, isScalar, MAP } from './identity.js' +import type { ParsedNode, Range } from './Node.js' import { Pair } from './Pair.js' import { isScalarValue, Scalar } from './Scalar.js' import type { ToJSContext } from './toJS.js' diff --git a/src/nodes/YAMLSeq.ts b/src/nodes/YAMLSeq.ts index 124f7cb6..6f45d589 100644 --- a/src/nodes/YAMLSeq.ts +++ b/src/nodes/YAMLSeq.ts @@ -3,7 +3,8 @@ import type { Schema } from '../schema/Schema.js' import type { StringifyContext } from '../stringify/stringify.js' import { stringifyCollection } from '../stringify/stringifyCollection.js' import { Collection } from './Collection.js' -import { isScalar, ParsedNode, Range, SEQ } from './Node.js' +import { isScalar, SEQ } from './identity.js' +import type { ParsedNode, Range } from './Node.js' import type { Pair } from './Pair.js' import { isScalarValue, Scalar } from './Scalar.js' import { toJS, ToJSContext } from './toJS.js' diff --git a/src/nodes/addPairToJSMap.ts b/src/nodes/addPairToJSMap.ts index a7b6345f..e390ebb0 100644 --- a/src/nodes/addPairToJSMap.ts +++ b/src/nodes/addPairToJSMap.ts @@ -1,6 +1,6 @@ import { warn } from '../log.js' import { createStringifyContext } from '../stringify/stringify.js' -import { isAlias, isMap, isNode, isScalar, isSeq } from './Node.js' +import { isAlias, isMap, isNode, isScalar, isSeq } from './identity.js' import type { Pair } from './Pair.js' import { Scalar } from './Scalar.js' import { toJS, ToJSContext } from './toJS.js' diff --git a/src/nodes/identity.ts b/src/nodes/identity.ts new file mode 100644 index 00000000..f7a6eb43 --- /dev/null +++ b/src/nodes/identity.ts @@ -0,0 +1,68 @@ +import type { Document } from '../doc/Document.js' +import type { Alias } from './Alias.js' +import type { Node } from './Node.js' +import type { Pair } from './Pair.js' +import type { Scalar } from './Scalar.js' +import type { YAMLMap } from './YAMLMap.js' +import type { YAMLSeq } from './YAMLSeq.js' + +export const ALIAS = Symbol.for('yaml.alias') +export const DOC = Symbol.for('yaml.document') +export const MAP = Symbol.for('yaml.map') +export const PAIR = Symbol.for('yaml.pair') +export const SCALAR = Symbol.for('yaml.scalar') +export const SEQ = Symbol.for('yaml.seq') +export const NODE_TYPE = Symbol.for('yaml.node.type') + +export const isAlias = (node: any): node is Alias => + !!node && typeof node === 'object' && node[NODE_TYPE] === ALIAS + +export const isDocument = ( + node: any +): node is Document => + !!node && typeof node === 'object' && node[NODE_TYPE] === DOC + +export const isMap = ( + node: any +): node is YAMLMap => + !!node && typeof node === 'object' && node[NODE_TYPE] === MAP + +export const isPair = ( + node: any +): node is Pair => + !!node && typeof node === 'object' && node[NODE_TYPE] === PAIR + +export const isScalar = (node: any): node is Scalar => + !!node && typeof node === 'object' && node[NODE_TYPE] === SCALAR + +export const isSeq = (node: any): node is YAMLSeq => + !!node && typeof node === 'object' && node[NODE_TYPE] === SEQ + +export function isCollection( + node: any +): node is YAMLMap | YAMLSeq { + if (node && typeof node === 'object') + switch (node[NODE_TYPE]) { + case MAP: + case SEQ: + return true + } + return false +} + +export function isNode(node: any): node is Node { + if (node && typeof node === 'object') + switch (node[NODE_TYPE]) { + case ALIAS: + case MAP: + case SCALAR: + case SEQ: + return true + } + return false +} + +export const hasAnchor = ( + node: unknown +): node is Scalar | YAMLMap | YAMLSeq => + (isScalar(node) || isCollection(node)) && !!node.anchor diff --git a/src/nodes/toJS.ts b/src/nodes/toJS.ts index f743d8bf..ac7d3944 100644 --- a/src/nodes/toJS.ts +++ b/src/nodes/toJS.ts @@ -1,6 +1,6 @@ import type { Document } from '../doc/Document.js' -import type { stringify } from '../stringify/stringify.js' -import { hasAnchor, Node } from './Node.js' +import { hasAnchor } from './identity.js' +import type { Node } from './Node.js' export interface AnchorData { aliasCount: number @@ -16,9 +16,6 @@ export interface ToJSContext { mapKeyWarned: boolean maxAliasCount: number onCreate?: (res: unknown) => void - - /** Requiring this directly in Pair would create circular dependencies */ - stringify: typeof stringify } /** diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts index 7f55ef6b..74ad2af0 100644 --- a/src/schema/Schema.ts +++ b/src/schema/Schema.ts @@ -1,4 +1,4 @@ -import { MAP, SCALAR, SEQ } from '../nodes/Node.js' +import { MAP, SCALAR, SEQ } from '../nodes/identity.js' import type { Pair } from '../nodes/Pair.js' import type { SchemaOptions, ToStringOptions } from '../options.js' import { map } from './common/map.js' diff --git a/src/schema/common/map.ts b/src/schema/common/map.ts index aab66b10..82159ed0 100644 --- a/src/schema/common/map.ts +++ b/src/schema/common/map.ts @@ -1,5 +1,5 @@ import type { CreateNodeContext } from '../../doc/createNode.js' -import { isMap } from '../../nodes/Node.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' diff --git a/src/schema/common/seq.ts b/src/schema/common/seq.ts index 8851012a..e908a7d6 100644 --- a/src/schema/common/seq.ts +++ b/src/schema/common/seq.ts @@ -1,5 +1,5 @@ import { CreateNodeContext, createNode } from '../../doc/createNode.js' -import { isSeq } from '../../nodes/Node.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' diff --git a/src/schema/yaml-1.1/omap.ts b/src/schema/yaml-1.1/omap.ts index c63ff198..94cc9342 100644 --- a/src/schema/yaml-1.1/omap.ts +++ b/src/schema/yaml-1.1/omap.ts @@ -1,7 +1,7 @@ -import { YAMLSeq } from '../../nodes/YAMLSeq.js' +import { isPair, isScalar } from '../../nodes/identity.js' import { toJS, ToJSContext } from '../../nodes/toJS.js' -import { isPair, isScalar } from '../../nodes/Node.js' import { YAMLMap } from '../../nodes/YAMLMap.js' +import { YAMLSeq } from '../../nodes/YAMLSeq.js' import { CollectionTag } from '../types.js' import { createPairs, resolvePairs } from './pairs.js' diff --git a/src/schema/yaml-1.1/pairs.ts b/src/schema/yaml-1.1/pairs.ts index fce3f9cb..c5dee951 100644 --- a/src/schema/yaml-1.1/pairs.ts +++ b/src/schema/yaml-1.1/pairs.ts @@ -1,5 +1,6 @@ import type { CreateNodeContext } from '../../doc/createNode.js' -import { isMap, isPair, isSeq, ParsedNode } from '../../nodes/Node.js' +import { isMap, isPair, isSeq } from '../../nodes/identity.js' +import type { ParsedNode } from '../../nodes/Node.js' import { createPair, Pair } from '../../nodes/Pair.js' import { Scalar } from '../../nodes/Scalar.js' import { YAMLMap } from '../../nodes/YAMLMap.js' diff --git a/src/schema/yaml-1.1/set.ts b/src/schema/yaml-1.1/set.ts index 97509767..e877527c 100644 --- a/src/schema/yaml-1.1/set.ts +++ b/src/schema/yaml-1.1/set.ts @@ -1,5 +1,5 @@ import type { Schema } from '../../schema/Schema.js' -import { isMap, isPair, isScalar } from '../../nodes/Node.js' +import { isMap, isPair, isScalar } from '../../nodes/identity.js' import { createPair, Pair } from '../../nodes/Pair.js' import { Scalar } from '../../nodes/Scalar.js' import { ToJSContext } from '../../nodes/toJS.js' diff --git a/src/stringify/stringify.ts b/src/stringify/stringify.ts index 2d544728..7a67f915 100644 --- a/src/stringify/stringify.ts +++ b/src/stringify/stringify.ts @@ -6,9 +6,9 @@ import { isCollection, isNode, isPair, - isScalar, - Node -} from '../nodes/Node.js' + isScalar +} from '../nodes/identity.js' +import type { Node } from '../nodes/Node.js' import type { Scalar } from '../nodes/Scalar.js' import type { ToStringOptions } from '../options.js' import type { CollectionTag, ScalarTag } from '../schema/types.js' diff --git a/src/stringify/stringifyCollection.ts b/src/stringify/stringifyCollection.ts index 7d9e780a..5d7f9088 100644 --- a/src/stringify/stringifyCollection.ts +++ b/src/stringify/stringifyCollection.ts @@ -1,5 +1,5 @@ import { Collection } from '../nodes/Collection.js' -import { isNode, isPair } from '../nodes/Node.js' +import { isNode, isPair } from '../nodes/identity.js' import { stringify, StringifyContext } from './stringify.js' import { indentComment, lineComment } from './stringifyComment.js' diff --git a/src/stringify/stringifyDocument.ts b/src/stringify/stringifyDocument.ts index a55e575f..ce7337b4 100644 --- a/src/stringify/stringifyDocument.ts +++ b/src/stringify/stringifyDocument.ts @@ -1,6 +1,7 @@ -import { Document } from '../doc/Document.js' -import { isNode, Node } from '../nodes/Node.js' -import { ToStringOptions } from '../options.js' +import type { Document } from '../doc/Document.js' +import { isNode } from '../nodes/identity.js' +import type { Node } from '../nodes/Node.js' +import type { ToStringOptions } from '../options.js' import { createStringifyContext, stringify, diff --git a/src/stringify/stringifyPair.ts b/src/stringify/stringifyPair.ts index 1c9bdb50..ad9fabcd 100644 --- a/src/stringify/stringifyPair.ts +++ b/src/stringify/stringifyPair.ts @@ -1,4 +1,4 @@ -import { isCollection, isNode, isScalar, isSeq } from '../nodes/Node.js' +import { isCollection, isNode, isScalar, isSeq } from '../nodes/identity.js' import type { Pair } from '../nodes/Pair.js' import { Scalar } from '../nodes/Scalar.js' import { stringify, StringifyContext } from './stringify.js' diff --git a/src/test-events.ts b/src/test-events.ts index 29459d46..ca7c6c83 100644 --- a/src/test-events.ts +++ b/src/test-events.ts @@ -6,10 +6,9 @@ import { isNode, isPair, isScalar, - isSeq, - Node, - ParsedNode -} from './nodes/Node.js' + isSeq +} from './nodes/identity.js' +import type { Node, ParsedNode } from './nodes/Node.js' import type { Pair } from './nodes/Pair.js' import { parseAllDocuments } from './public-api.js' import { visit } from './visit.js' diff --git a/src/visit.ts b/src/visit.ts index 6ceb981a..e5291b36 100644 --- a/src/visit.ts +++ b/src/visit.ts @@ -8,9 +8,9 @@ import { isNode, isPair, isScalar, - isSeq, - Node -} from './nodes/Node.js' + isSeq +} from './nodes/identity.js' +import { Node } from './nodes/Node.js' import type { Pair } from './nodes/Pair.js' import type { Scalar } from './nodes/Scalar.js' import type { YAMLMap } from './nodes/YAMLMap.js' From 7f9707287d608efbf5ced95c796cd305e375dbe3 Mon Sep 17 00:00:00 2001 From: Eemeli Aro Date: Wed, 15 Mar 2023 14:04:18 +0200 Subject: [PATCH 2/2] feat: Add a toJS(doc) method to nodes --- .eslintrc.yaml | 5 +- docs/03_options.md | 2 +- docs/05_content_nodes.md | 3 +- src/nodes/Alias.ts | 9 +++- src/nodes/Node.ts | 25 ++++++++++ tests/node-to-js.ts | 100 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 tests/node-to-js.ts diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 24017a94..84a47d26 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -50,9 +50,6 @@ overrides: rules: camelcase: 0 '@typescript-eslint/no-non-null-assertion': off - '@typescript-eslint/no-unsafe-return': off - - - files: [tests/doc/**] - rules: '@typescript-eslint/no-unsafe-call': off + '@typescript-eslint/no-unsafe-return': off '@typescript-eslint/unbound-method': off diff --git a/docs/03_options.md b/docs/03_options.md index f1cb53d1..b19201be 100644 --- a/docs/03_options.md +++ b/docs/03_options.md @@ -119,7 +119,7 @@ parse('{[1, 2]: many}', { mapAsMap: true }) // Map { [ 1, 2 ] => 'many' } These options influence how the document is transformed into "native" JavaScript representation. -Used by: `parse()` and `doc.toJS()` +Used by: `parse()`, `doc.toJS()` and `node.toJS()` | Name | Type | Default | Description | | ------------- | ------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | diff --git a/docs/05_content_nodes.md b/docs/05_content_nodes.md index 5b7b9633..862f8963 100644 --- a/docs/05_content_nodes.md +++ b/docs/05_content_nodes.md @@ -21,7 +21,8 @@ class NodeBase { // a blank line before this node and its commentBefore tag?: string // a fully qualified tag, if required clone(): NodeBase // a copy of this node - toJSON(): any // a plain JS or JSON representation of this node + toJS(doc, options?): any // a plain JS representation of this node + toJSON(): any // a plain JSON representation of this node } ``` diff --git a/src/nodes/Alias.ts b/src/nodes/Alias.ts index 213a667a..b4bb5b3f 100644 --- a/src/nodes/Alias.ts +++ b/src/nodes/Alias.ts @@ -6,7 +6,7 @@ import { visit } from '../visit.js' import { ALIAS, isAlias, isCollection, isPair } from './identity.js' import { Node, NodeBase, Range } from './Node.js' import type { Scalar } from './Scalar' -import type { ToJSContext } from './toJS.js' +import { toJS, ToJSContext } from './toJS.js' import type { YAMLMap } from './YAMLMap.js' import type { YAMLSeq } from './YAMLSeq.js' @@ -55,7 +55,12 @@ export class Alias extends NodeBase { const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}` throw new ReferenceError(msg) } - const data = anchors.get(source) + let data = anchors.get(source) + if (!data) { + // Resolve anchors for Node.prototype.toJS() + toJS(source, null, ctx) + data = anchors.get(source) + } /* istanbul ignore if */ if (!data || data.res === undefined) { const msg = 'This should not happen: Alias anchor was not resolved?' diff --git a/src/nodes/Node.ts b/src/nodes/Node.ts index 355215de..829d20b7 100644 --- a/src/nodes/Node.ts +++ b/src/nodes/Node.ts @@ -1,8 +1,12 @@ +import { applyReviver } from '../doc/applyReviver.js' +import type { Document } from '../doc/Document.js' +import type { ToJSOptions } from '../options.js' import { Token } from '../parse/cst.js' import type { StringifyContext } from '../stringify/stringify.js' import type { Alias } from './Alias.js' import { NODE_TYPE } from './identity.js' import type { Scalar } from './Scalar.js' +import { toJS, ToJSContext } from './toJS.js' import type { YAMLMap } from './YAMLMap.js' import type { YAMLSeq } from './YAMLSeq.js' @@ -87,4 +91,25 @@ export abstract class NodeBase { if (this.range) copy.range = this.range.slice() as NodeBase['range'] return copy } + + /** A plain JavaScript representation of this node. */ + toJS( + doc: Document, + { mapAsMap, maxAliasCount, onAnchor, reviver }: ToJSOptions = {} + ): any { + const ctx: ToJSContext = { + anchors: new Map(), + doc, + keep: true, + mapAsMap: mapAsMap === true, + mapKeyWarned: false, + maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100 + } + const res = toJS(this, '', ctx) + if (typeof onAnchor === 'function') + for (const { count, res } of ctx.anchors.values()) onAnchor(res, count) + return typeof reviver === 'function' + ? applyReviver(reviver, { '': res }, '', res) + : res + } } diff --git a/tests/node-to-js.ts b/tests/node-to-js.ts new file mode 100644 index 00000000..41beed96 --- /dev/null +++ b/tests/node-to-js.ts @@ -0,0 +1,100 @@ +import { parseDocument, YAMLMap, YAMLSeq } from 'yaml' +import { source } from './_utils' + +describe('scalars', () => { + test('plain', () => { + const doc = parseDocument('42') + expect(doc.contents?.toJS(doc)).toBe(42) + }) + + test('plain in map', () => { + const doc = parseDocument('key: 42') + expect(doc.get('key', true).toJS(doc)).toBe(42) + }) +}) + +describe('collections', () => { + test('map', () => { + const doc = parseDocument('key: 42') + expect(doc.contents?.toJS(doc)).toMatchObject({ key: 42 }) + }) + + test('map in seq', () => { + const doc = parseDocument('- key: 42') + expect(doc.get(0).toJS(doc)).toMatchObject({ key: 42 }) + }) +}) + +describe('alias', () => { + test('simple', () => { + const doc = parseDocument(source` + one: &a 42 + two: *a + `) + expect(doc.get('two').toJS(doc)).toBe(42) + }) + + test('repeated identifier', () => { + const doc = parseDocument(source` + one: &a 13 + two: &a 42 + three: *a + four: &a 99 + `) + expect(doc.get('three').toJS(doc)).toBe(42) + }) + + test('nested aliases', () => { + const doc = parseDocument(source` + one: &a 42 + two: &b { key: *a } + three: *b + `) + expect(doc.get('three').toJS(doc)).toMatchObject({ key: 42 }) + }) + + test('missing anchor', () => { + const doc = parseDocument(source` + one: &a 42 + two: *b + `) + expect(() => doc.get('two').toJS(doc)).toThrow(ReferenceError) + }) +}) + +describe('options', () => { + test('mapAsMap', () => { + const doc = parseDocument('key: 42') + expect(doc.contents?.toJS(doc, { mapAsMap: true })).toMatchObject( + new Map([['key', 42]]) + ) + }) + + test('onAnchor', () => { + const doc = parseDocument(source` + one: &a 42 + two: &b { key: *a } + three: *b + `) + const onAnchor = jest.fn() + doc.get('three').toJS(doc, { onAnchor }) + expect(onAnchor.mock.calls).toMatchObject([ + [{ key: 42 }, 2], + [42, 2] + ]) + }) + + test('reviver', () => { + const doc = parseDocument(source` + one: &a 42 + two: &b { key: *a } + three: *b + `) + const reviver = jest.fn((_key, value) => value) + doc.get('three').toJS(doc, { reviver }) + expect(reviver.mock.calls).toMatchObject([ + ['key', 42], + ['', { key: 42 }] + ]) + }) +})