You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
72 lines
2.2 KiB
72 lines
2.2 KiB
import { isScalar, isCollection } from '../nodes/Node.js'; |
|
import { visit } from '../visit.js'; |
|
|
|
/** |
|
* Verify that the input string is a valid anchor. |
|
* |
|
* Will throw on errors. |
|
*/ |
|
function anchorIsValid(anchor) { |
|
if (/[\x00-\x19\s,[\]{}]/.test(anchor)) { |
|
const sa = JSON.stringify(anchor); |
|
const msg = `Anchor must not contain whitespace or control characters: ${sa}`; |
|
throw new Error(msg); |
|
} |
|
return true; |
|
} |
|
function anchorNames(root) { |
|
const anchors = new Set(); |
|
visit(root, { |
|
Value(_key, node) { |
|
if (node.anchor) |
|
anchors.add(node.anchor); |
|
} |
|
}); |
|
return anchors; |
|
} |
|
/** Find a new anchor name with the given `prefix` and a one-indexed suffix. */ |
|
function findNewAnchor(prefix, exclude) { |
|
for (let i = 1; true; ++i) { |
|
const name = `${prefix}${i}`; |
|
if (!exclude.has(name)) |
|
return name; |
|
} |
|
} |
|
function createNodeAnchors(doc, prefix) { |
|
const aliasObjects = []; |
|
const sourceObjects = new Map(); |
|
let prevAnchors = null; |
|
return { |
|
onAnchor: (source) => { |
|
aliasObjects.push(source); |
|
if (!prevAnchors) |
|
prevAnchors = anchorNames(doc); |
|
const anchor = findNewAnchor(prefix, prevAnchors); |
|
prevAnchors.add(anchor); |
|
return anchor; |
|
}, |
|
/** |
|
* With circular references, the source node is only resolved after all |
|
* of its child nodes are. This is why anchors are set only after all of |
|
* the nodes have been created. |
|
*/ |
|
setAnchors: () => { |
|
for (const source of aliasObjects) { |
|
const ref = sourceObjects.get(source); |
|
if (typeof ref === 'object' && |
|
ref.anchor && |
|
(isScalar(ref.node) || isCollection(ref.node))) { |
|
ref.node.anchor = ref.anchor; |
|
} |
|
else { |
|
const error = new Error('Failed to resolve repeated object (this should not happen)'); |
|
error.source = source; |
|
throw error; |
|
} |
|
} |
|
}, |
|
sourceObjects |
|
}; |
|
} |
|
|
|
export { anchorIsValid, anchorNames, createNodeAnchors, findNewAnchor };
|
|
|