Skip to content

Commit 13a141c

Browse files
committed
feat: Build cached anchor/alias node list for faster alias resolving
1 parent 9cf06d2 commit 13a141c

3 files changed

Lines changed: 32 additions & 4 deletions

File tree

src/doc/Document.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,8 @@ export class Document<
433433
keep: !json,
434434
mapAsMap: mapAsMap === true,
435435
mapKeyWarned: false,
436-
maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100
436+
maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100,
437+
anchorAndAliasNodes: []
437438
}
438439
const res = toJS(this.contents, jsonArg ?? '', ctx)
439440
if (typeof onAnchor === 'function')

src/nodes/Alias.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,17 @@ export class Alias extends NodeBase {
5252
toJSON(_arg?: unknown, ctx?: ToJSContext): unknown {
5353
if (!ctx) return { source: this.source }
5454
const { anchors, doc, maxAliasCount } = ctx
55-
const source = this.resolve(doc)
55+
56+
let source: Scalar | YAMLMap | YAMLSeq | undefined = undefined
57+
if (ctx.anchorAndAliasNodes) {
58+
for (const node of ctx.anchorAndAliasNodes) {
59+
if (node === this) break
60+
if (node.anchor === this.source) source = node
61+
}
62+
} else {
63+
source = this.resolve(doc)
64+
}
65+
5666
if (!source) {
5767
const msg = `Unresolved alias (the anchor must be set before the alias): ${this.source}`
5868
throw new ReferenceError(msg)

src/nodes/toJS.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { Document } from '../doc/Document.ts'
2-
import { hasAnchor } from './identity.ts'
2+
import { hasAnchor, isAlias } from './identity.ts'
33
import type { Node } from './Node.ts'
4+
import type { Alias } from './Alias.ts'
5+
import type { Scalar } from './Scalar.ts'
6+
import type { YAMLMap } from './YAMLMap.ts'
7+
import type { YAMLSeq } from './YAMLSeq.ts'
48

59
export interface AnchorData {
610
aliasCount: number
@@ -10,6 +14,8 @@ export interface AnchorData {
1014

1115
export interface ToJSContext {
1216
anchors: Map<Node, AnchorData>
17+
/** Cached anchor and allias nodes in the order they occurred in the document */
18+
anchorAndAliasNodes?: (Alias | Scalar | YAMLMap | YAMLSeq)[]
1319
doc: Document<Node, boolean>
1420
keep: boolean
1521
mapAsMap: boolean
@@ -33,13 +39,24 @@ export function toJS(value: any, arg: string | null, ctx?: ToJSContext): any {
3339
if (Array.isArray(value)) return value.map((v, i) => toJS(v, String(i), ctx))
3440
if (value && typeof value.toJSON === 'function') {
3541
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
36-
if (!ctx || !hasAnchor(value)) return value.toJSON(arg, ctx)
42+
if (!ctx) return value.toJSON(arg, ctx)
43+
44+
if (!hasAnchor(value)) {
45+
if (ctx.anchorAndAliasNodes && isAlias(value))
46+
ctx.anchorAndAliasNodes.push(value)
47+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
48+
return value.toJSON(arg, ctx)
49+
}
50+
3751
const data: AnchorData = { aliasCount: 0, count: 1, res: undefined }
3852
ctx.anchors.set(value, data)
3953
ctx.onCreate = res => {
4054
data.res = res
4155
delete ctx.onCreate
4256
}
57+
58+
if (ctx.anchorAndAliasNodes) ctx.anchorAndAliasNodes.push(value)
59+
4360
const res = value.toJSON(arg, ctx)
4461
if (ctx.onCreate) ctx.onCreate(res)
4562
return res

0 commit comments

Comments
 (0)