Skip to content

Commit

Permalink
feat: Require unique anchors during composition
Browse files Browse the repository at this point in the history
  • Loading branch information
eemeli committed Apr 20, 2022
1 parent e001fa9 commit 3371e5a
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 2 deletions.
1 change: 1 addition & 0 deletions src/compose/compose-doc.ts
Expand Up @@ -24,6 +24,7 @@ export function composeDoc(
const opts = Object.assign({ directives }, options)
const doc = new Document(undefined, opts) as Document.Parsed
const ctx: ComposeContext = {
anchors: options.version === 'next' ? new Map() : null,
atRoot: true,
directives: doc.directives,
options: doc.options,
Expand Down
23 changes: 21 additions & 2 deletions src/compose/compose-node.ts
Expand Up @@ -11,6 +11,7 @@ import { resolveEnd } from './resolve-end.js'
import { emptyScalarPosition } from './util-empty-scalar-position.js'

export interface ComposeContext {
anchors: Map<string, ParsedNode> | null
atRoot: boolean
directives: Directives
options: Readonly<Required<Omit<ParseOptions, 'lineCounter'>>>
Expand Down Expand Up @@ -51,13 +52,13 @@ export function composeNode(
case 'double-quoted-scalar':
case 'block-scalar':
node = composeScalar(ctx, token, tag, onError)
if (anchor) node.anchor = anchor.source.substring(1)
if (anchor) setAnchor(ctx, node, anchor, onError)
break
case 'block-map':
case 'block-seq':
case 'flow-collection':
node = composeCollection(CN, ctx, token, tag, onError)
if (anchor) node.anchor = anchor.source.substring(1)
if (anchor) setAnchor(ctx, node, anchor, onError)
break
default: {
const message =
Expand Down Expand Up @@ -134,3 +135,21 @@ function composeAlias(
if (re.comment) alias.comment = re.comment
return alias as Alias.Parsed
}

function setAnchor(
{ anchors }: ComposeContext,
node: ParsedNode,
anchor: SourceToken,
onError: ComposeErrorHandler
) {
const name = anchor.source.substring(1)
if (anchors) {
if (anchors.has(name)) {
const msg = `Anchors must be unique, ${name} is repeated`
onError(node.range, 'DUPLICATE_ANCHOR', msg)
} else {
anchors.set(name, node)
}
}
node.anchor = name
}
1 change: 1 addition & 0 deletions src/errors.ts
Expand Up @@ -10,6 +10,7 @@ export type ErrorCode =
| 'BAD_SCALAR_START'
| 'BLOCK_AS_IMPLICIT_KEY'
| 'BLOCK_IN_FLOW'
| 'DUPLICATE_ANCHOR'
| 'DUPLICATE_KEY'
| 'IMPOSSIBLE'
| 'KEY_OVER_1024_CHARS'
Expand Down
21 changes: 21 additions & 0 deletions tests/next.ts
Expand Up @@ -53,3 +53,24 @@ describe('relative-path alias', () => {
expect(() => doc.toJS()).toThrow(ReferenceError)
})
})

describe('unique anchors', () => {
test('repeats are fine without flag', () => {
const src = source`
- &a 1
- &a 2
- *a
`
expect(parse(src)).toEqual([1, 2, 2])
})

test("repeats are an error with 'next'", () => {
const src = source`
- &a 1
- &a 2
- *a
`
const doc = parseDocument(src, { version: 'next' })
expect(doc.errors).toMatchObject([{ code: 'DUPLICATE_ANCHOR' }])
})
})

0 comments on commit 3371e5a

Please sign in to comment.