forked from eemeli/yaml
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
compose-collection.ts
126 lines (115 loc) · 3.53 KB
/
compose-collection.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { isNode } from '../nodes/identity.js'
import type { ParsedNode } from '../nodes/Node.js'
import { Scalar } from '../nodes/Scalar.js'
import { YAMLMap } from '../nodes/YAMLMap.js'
import { YAMLSeq } from '../nodes/YAMLSeq.js'
import type {
BlockMap,
BlockSequence,
FlowCollection,
SourceToken
} from '../parse/cst.js'
import { CollectionTag } from '../schema/types.js'
import type { ComposeContext, ComposeNode } from './compose-node.js'
import type { ComposeErrorHandler } from './composer.js'
import { resolveBlockMap } from './resolve-block-map.js'
import { resolveBlockSeq } from './resolve-block-seq.js'
import { resolveFlowCollection } from './resolve-flow-collection.js'
function resolveCollection(
CN: ComposeNode,
ctx: ComposeContext,
token: BlockMap | BlockSequence | FlowCollection,
onError: ComposeErrorHandler,
tagName: string | null,
tag?: CollectionTag
) {
const coll =
token.type === 'block-map'
? resolveBlockMap(CN, ctx, token, onError, tag)
: token.type === 'block-seq'
? resolveBlockSeq(CN, ctx, token, onError, tag)
: resolveFlowCollection(CN, ctx, token, onError, tag)
const Coll = coll.constructor as typeof YAMLMap | typeof YAMLSeq
// If we got a tagName matching the class, or the tag name is '!',
// then use the tagName from the node class used to create it.
if (tagName === '!' || tagName === Coll.tagName) {
coll.tag = Coll.tagName
return coll
}
if (tagName) coll.tag = tagName
return coll
}
export function composeCollection(
CN: ComposeNode,
ctx: ComposeContext,
token: BlockMap | BlockSequence | FlowCollection,
tagToken: SourceToken | null,
onError: ComposeErrorHandler
) {
const tagName: string | null = !tagToken
? null
: ctx.directives.tagName(tagToken.source, msg =>
onError(tagToken, 'TAG_RESOLVE_FAILED', msg)
)
const expType: 'map' | 'seq' =
token.type === 'block-map'
? 'map'
: token.type === 'block-seq'
? 'seq'
: token.start.source === '{'
? 'map'
: 'seq'
// shortcut: check if it's a generic YAMLMap or YAMLSeq
// before jumping into the custom tag logic.
if (
!tagToken ||
!tagName ||
tagName === '!' ||
(tagName === YAMLMap.tagName && expType === 'map') ||
(tagName === YAMLSeq.tagName && expType === 'seq') ||
!expType
) {
return resolveCollection(CN, ctx, token, onError, tagName)
}
let tag = ctx.schema.tags.find(
t => t.tag === tagName && t.collection === expType
) as CollectionTag | undefined
if (!tag) {
const kt = ctx.schema.knownTags[tagName]
if (kt && kt.collection === expType) {
ctx.schema.tags.push(Object.assign({}, kt, { default: false }))
tag = kt
} else {
if (kt?.collection) {
onError(
tagToken,
'BAD_COLLECTION_TYPE',
`${kt.tag} used for ${expType} collection, but expects ${kt.collection}`,
true
)
} else {
onError(
tagToken,
'TAG_RESOLVE_FAILED',
`Unresolved tag: ${tagName}`,
true
)
}
return resolveCollection(CN, ctx, token, onError, tagName)
}
}
const coll = resolveCollection(CN, ctx, token, onError, tagName, tag)
const res =
tag.resolve?.(
coll,
msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg),
ctx.options
) ?? coll
const node = isNode(res)
? (res as ParsedNode)
: (new Scalar(res) as Scalar.Parsed)
node.range = coll.range
node.tag = tagName
if (tag?.format) (node as Scalar).format = tag.format
return node
}