-
Notifications
You must be signed in to change notification settings - Fork 151
Expand file tree
/
Copy pathAlias.ts
More file actions
139 lines (129 loc) · 3.94 KB
/
Alias.ts
File metadata and controls
139 lines (129 loc) · 3.94 KB
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
127
128
129
130
131
132
133
134
135
136
137
138
139
import { anchorIsValid } from '../doc/anchors.ts'
import type { Document } from '../doc/Document.ts'
import type { FlowScalar } from '../parse/cst.ts'
import type { StringifyContext } from '../stringify/stringify.ts'
import { visit } from '../visit.ts'
import { ALIAS, hasAnchor, isAlias, isCollection, isPair } from './identity.ts'
import type { Node, Range } from './Node.ts'
import { NodeBase } from './Node.ts'
import type { Scalar } from './Scalar.ts'
import type { ToJSContext } from './toJS.ts'
import { toJS } from './toJS.ts'
import type { YAMLMap } from './YAMLMap.ts'
import type { YAMLSeq } from './YAMLSeq.ts'
export declare namespace Alias {
interface Parsed extends Alias {
range: Range
srcToken?: FlowScalar & { type: 'alias' }
}
}
export class Alias extends NodeBase {
source: string
declare anchor?: never
constructor(source: string) {
super(ALIAS)
this.source = source
Object.defineProperty(this, 'tag', {
set() {
throw new Error('Alias nodes cannot have tags')
}
})
}
/**
* Resolve the value of this alias within `doc`, finding the last
* instance of the `source` anchor before this node.
*/
resolve(
doc: Document,
ctx?: ToJSContext
): Scalar | YAMLMap | YAMLSeq | undefined {
let nodes: Node[]
if (ctx?.aliasResolveCache) {
nodes = ctx.aliasResolveCache
} else {
nodes = []
visit(doc, {
Node: (_key: unknown, node: Node) => {
if (isAlias(node) || hasAnchor(node)) nodes.push(node)
}
})
if (ctx) ctx.aliasResolveCache = nodes
}
let found: Scalar | YAMLMap | YAMLSeq | undefined = undefined
for (const node of nodes) {
if (node === this) break
if (node.anchor === this.source) found = node
}
return found
}
toJSON(_arg?: unknown, ctx?: ToJSContext): unknown {
if (!ctx) return { source: this.source }
const { anchors, doc, maxAliasCount } = ctx
const source = this.resolve(doc, ctx)
if (!source) {
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`
throw new ReferenceError(msg)
}
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?'
throw new ReferenceError(msg)
}
if (maxAliasCount >= 0) {
data.count += 1
if (data.aliasCount === 0)
data.aliasCount = getAliasCount(doc, source, anchors)
if (data.count * data.aliasCount > maxAliasCount) {
const msg =
'Excessive alias count indicates a resource exhaustion attack'
throw new ReferenceError(msg)
}
}
return data.res
}
toString(
ctx?: StringifyContext,
_onComment?: () => void,
_onChompKeep?: () => void
): string {
const src = `*${this.source}`
if (ctx) {
anchorIsValid(this.source)
if (ctx.options.verifyAliasOrder && !ctx.anchors.has(this.source)) {
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`
throw new Error(msg)
}
if (ctx.implicitKey) return `${src} `
}
return src
}
}
function getAliasCount(
doc: Document,
node: unknown,
anchors: ToJSContext['anchors']
): number {
if (isAlias(node)) {
const source = node.resolve(doc)
const anchor = anchors && source && anchors.get(source)
return anchor ? anchor.count * anchor.aliasCount : 0
} else if (isCollection(node)) {
let count = 0
for (const item of node.items) {
const c = getAliasCount(doc, item, anchors)
if (c > count) count = c
}
return count
} else if (isPair(node)) {
const kc = getAliasCount(doc, node.key, anchors)
const vc = getAliasCount(doc, node.value, anchors)
return Math.max(kc, vc)
}
return 1
}