-
-
Notifications
You must be signed in to change notification settings - Fork 119
/
Copy pathcompose-scalar.ts
113 lines (105 loc) · 3.41 KB
/
compose-scalar.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
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'
import type { ScalarTag } from '../schema/types.js'
import type { ComposeContext } from './compose-node.js'
import type { ComposeErrorHandler } from './composer.js'
import { resolveBlockScalar } from './resolve-block-scalar.js'
import { resolveFlowScalar } from './resolve-flow-scalar.js'
export function composeScalar(
ctx: ComposeContext,
token: FlowScalar | BlockScalar,
tagToken: SourceToken | null,
onError: ComposeErrorHandler
) {
const { value, type, comment, range } =
token.type === 'block-scalar'
? resolveBlockScalar(ctx, token, onError)
: resolveFlowScalar(token, ctx.options.strict, onError)
const tagName = tagToken
? ctx.directives.tagName(tagToken.source, msg =>
onError(tagToken, 'TAG_RESOLVE_FAILED', msg)
)
: null
const tag =
tagToken && tagName
? findScalarTagByName(ctx.schema, value, tagName, tagToken, onError)
: token.type === 'scalar'
? findScalarTagByTest(ctx, value, token, onError)
: ctx.schema[SCALAR]
let scalar: Scalar
try {
const res = tag.resolve(
value,
msg => onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg),
ctx.options
)
scalar = isScalar(res) ? res : new Scalar(res)
} catch (error) {
const msg = error instanceof Error ? error.message : String(error)
onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg)
scalar = new Scalar(value)
}
scalar.range = range
scalar.source = value
if (type) scalar.type = type
if (tagName) scalar.tag = tagName
if (tag.format) scalar.format = tag.format
if (comment) scalar.comment = comment
return scalar as Scalar.Parsed
}
function findScalarTagByName(
schema: Schema,
value: string,
tagName: string,
tagToken: SourceToken,
onError: ComposeErrorHandler
) {
if (tagName === '!') return schema[SCALAR] // non-specific tag
const matchWithTest: ScalarTag[] = []
for (const tag of schema.tags) {
if (!tag.collection && tag.tag === tagName) {
if (tag.default && tag.test) matchWithTest.push(tag)
else return tag
}
}
for (const tag of matchWithTest) if (tag.test?.test(value)) return tag
const kt = schema.knownTags[tagName]
if (kt && !kt.collection) {
// Ensure that the known tag is available for stringifying,
// but does not get used by default.
schema.tags.push(Object.assign({}, kt, { default: false, test: undefined }))
return kt
}
onError(
tagToken,
'TAG_RESOLVE_FAILED',
`Unresolved tag: ${tagName}`,
tagName !== 'tag:yaml.org,2002:str'
)
return schema[SCALAR]
}
function findScalarTagByTest(
{ directives, schema }: ComposeContext,
value: string,
token: FlowScalar,
onError: ComposeErrorHandler
) {
const tag =
(schema.tags.find(
tag => tag.default && tag.test?.test(value)
) as ScalarTag) || schema[SCALAR]
if (schema.compat) {
const compat =
schema.compat.find(tag => tag.default && tag.test?.test(value)) ??
schema[SCALAR]
if (tag.tag !== compat.tag) {
const ts = directives.tagString(tag.tag)
const cs = directives.tagString(compat.tag)
const msg = `Value may be parsed as either ${ts} or ${cs}`
onError(token, 'TAG_RESOLVE_FAILED', msg, true)
}
}
return tag
}