Skip to content

Commit e8cf6f6

Browse files
committed
Fix error with anchor not being assigned to an empty node
fix #301
1 parent a583097 commit e8cf6f6

File tree

3 files changed

+77
-45
lines changed

3 files changed

+77
-45
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4747
- Removed `bower.json`.
4848
- Tags are now url-decoded in `load()` and url-encoded in `dump()`
4949
(previously usage of custom non-ascii tags may have led to invalid YAML that can't be parsed).
50+
- Anchors now work correctly with empty nodes, #301.
5051

5152

5253
## [3.14.1] - 2020-12-07

lib/loader.js

+48-45
Original file line numberDiff line numberDiff line change
@@ -1448,61 +1448,64 @@ function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact
14481448
}
14491449
}
14501450

1451-
if (state.tag !== null && state.tag !== '!') {
1452-
if (state.tag === '?') {
1453-
// Implicit resolving is not allowed for non-scalar types, and '?'
1454-
// non-specific tag is only automatically assigned to plain scalars.
1455-
//
1456-
// We only need to check kind conformity in case user explicitly assigns '?'
1457-
// tag, for example like this: "!<?> [0]"
1458-
//
1459-
if (state.result !== null && state.kind !== 'scalar') {
1460-
throwError(state, 'unacceptable node kind for !<?> tag; it should be "scalar", not "' + state.kind + '"');
1461-
}
1451+
if (state.tag === null) {
1452+
if (state.anchor !== null) {
1453+
state.anchorMap[state.anchor] = state.result;
1454+
}
14621455

1463-
for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
1464-
type = state.implicitTypes[typeIndex];
1456+
} else if (state.tag === '?') {
1457+
// Implicit resolving is not allowed for non-scalar types, and '?'
1458+
// non-specific tag is only automatically assigned to plain scalars.
1459+
//
1460+
// We only need to check kind conformity in case user explicitly assigns '?'
1461+
// tag, for example like this: "!<?> [0]"
1462+
//
1463+
if (state.result !== null && state.kind !== 'scalar') {
1464+
throwError(state, 'unacceptable node kind for !<?> tag; it should be "scalar", not "' + state.kind + '"');
1465+
}
14651466

1466-
if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
1467-
state.result = type.construct(state.result);
1468-
state.tag = type.tag;
1469-
if (state.anchor !== null) {
1470-
state.anchorMap[state.anchor] = state.result;
1471-
}
1472-
break;
1467+
for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
1468+
type = state.implicitTypes[typeIndex];
1469+
1470+
if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
1471+
state.result = type.construct(state.result);
1472+
state.tag = type.tag;
1473+
if (state.anchor !== null) {
1474+
state.anchorMap[state.anchor] = state.result;
14731475
}
1476+
break;
14741477
}
1478+
}
1479+
} else if (state.tag !== '!') {
1480+
if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
1481+
type = state.typeMap[state.kind || 'fallback'][state.tag];
14751482
} else {
1476-
if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
1477-
type = state.typeMap[state.kind || 'fallback'][state.tag];
1478-
} else {
1479-
// looking for multi type
1480-
type = null;
1481-
typeList = state.typeMap.multi[state.kind || 'fallback'];
1482-
1483-
for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
1484-
if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {
1485-
type = typeList[typeIndex];
1486-
break;
1487-
}
1483+
// looking for multi type
1484+
type = null;
1485+
typeList = state.typeMap.multi[state.kind || 'fallback'];
1486+
1487+
for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
1488+
if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {
1489+
type = typeList[typeIndex];
1490+
break;
14881491
}
14891492
}
1493+
}
14901494

1491-
if (!type) {
1492-
throwError(state, 'unknown tag !<' + state.tag + '>');
1493-
}
1495+
if (!type) {
1496+
throwError(state, 'unknown tag !<' + state.tag + '>');
1497+
}
14941498

1495-
if (state.result !== null && type.kind !== state.kind) {
1496-
throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
1497-
}
1499+
if (state.result !== null && type.kind !== state.kind) {
1500+
throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
1501+
}
14981502

1499-
if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched
1500-
throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
1501-
} else {
1502-
state.result = type.construct(state.result, state.tag);
1503-
if (state.anchor !== null) {
1504-
state.anchorMap[state.anchor] = state.result;
1505-
}
1503+
if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched
1504+
throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
1505+
} else {
1506+
state.result = type.construct(state.result, state.tag);
1507+
if (state.anchor !== null) {
1508+
state.anchorMap[state.anchor] = state.result;
15061509
}
15071510
}
15081511
}

test/issues/0301.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
4+
const assert = require('assert');
5+
const yaml = require('../../');
6+
7+
8+
it('should assign anchor to an empty node', function () {
9+
assert.deepStrictEqual(
10+
yaml.load('foo: &a\nbar: *a\n'),
11+
{ foo: null, bar: null }
12+
);
13+
14+
assert.deepStrictEqual(
15+
yaml.load('{ foo: &a, bar: *a }'),
16+
{ foo: null, bar: null }
17+
);
18+
19+
assert.deepStrictEqual(
20+
yaml.load('- &a\n- *a\n'),
21+
[ null, null ]
22+
);
23+
24+
assert.deepStrictEqual(
25+
yaml.load('[ &a, *a ]'),
26+
[ null, null ]
27+
);
28+
});

0 commit comments

Comments
 (0)