Skip to content

Commit

Permalink
Fix error with anchor not being assigned to an empty node
Browse files Browse the repository at this point in the history
  • Loading branch information
rlidwka committed Dec 29, 2020
1 parent a583097 commit e8cf6f6
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 45 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed `bower.json`.
- Tags are now url-decoded in `load()` and url-encoded in `dump()`
(previously usage of custom non-ascii tags may have led to invalid YAML that can't be parsed).
- Anchors now work correctly with empty nodes, #301.


## [3.14.1] - 2020-12-07
Expand Down
93 changes: 48 additions & 45 deletions lib/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1448,61 +1448,64 @@ function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact
}
}

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

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

if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
state.result = type.construct(state.result);
state.tag = type.tag;
if (state.anchor !== null) {
state.anchorMap[state.anchor] = state.result;
}
break;
for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) {
type = state.implicitTypes[typeIndex];

if (type.resolve(state.result)) { // `state.result` updated in resolver if matched
state.result = type.construct(state.result);
state.tag = type.tag;
if (state.anchor !== null) {
state.anchorMap[state.anchor] = state.result;
}
break;
}
}
} else if (state.tag !== '!') {
if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
type = state.typeMap[state.kind || 'fallback'][state.tag];
} else {
if (_hasOwnProperty.call(state.typeMap[state.kind || 'fallback'], state.tag)) {
type = state.typeMap[state.kind || 'fallback'][state.tag];
} else {
// looking for multi type
type = null;
typeList = state.typeMap.multi[state.kind || 'fallback'];

for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {
type = typeList[typeIndex];
break;
}
// looking for multi type
type = null;
typeList = state.typeMap.multi[state.kind || 'fallback'];

for (typeIndex = 0, typeQuantity = typeList.length; typeIndex < typeQuantity; typeIndex += 1) {
if (state.tag.slice(0, typeList[typeIndex].tag.length) === typeList[typeIndex].tag) {
type = typeList[typeIndex];
break;
}
}
}

if (!type) {
throwError(state, 'unknown tag !<' + state.tag + '>');
}
if (!type) {
throwError(state, 'unknown tag !<' + state.tag + '>');
}

if (state.result !== null && type.kind !== state.kind) {
throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
}
if (state.result !== null && type.kind !== state.kind) {
throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"');
}

if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched
throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
} else {
state.result = type.construct(state.result, state.tag);
if (state.anchor !== null) {
state.anchorMap[state.anchor] = state.result;
}
if (!type.resolve(state.result, state.tag)) { // `state.result` updated in resolver if matched
throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag');
} else {
state.result = type.construct(state.result, state.tag);
if (state.anchor !== null) {
state.anchorMap[state.anchor] = state.result;
}
}
}
Expand Down
28 changes: 28 additions & 0 deletions test/issues/0301.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';


const assert = require('assert');
const yaml = require('../../');


it('should assign anchor to an empty node', function () {
assert.deepStrictEqual(
yaml.load('foo: &a\nbar: *a\n'),
{ foo: null, bar: null }
);

assert.deepStrictEqual(
yaml.load('{ foo: &a, bar: *a }'),
{ foo: null, bar: null }
);

assert.deepStrictEqual(
yaml.load('- &a\n- *a\n'),
[ null, null ]
);

assert.deepStrictEqual(
yaml.load('[ &a, *a ]'),
[ null, null ]
);
});

0 comments on commit e8cf6f6

Please sign in to comment.