myst-parser
Version:
Markdown parser for MyST markdown in JavaScript
91 lines (90 loc) • 3.82 kB
JavaScript
import { fileError, fileWarn, RuleId } from 'myst-common';
import { contentFromNode } from './utils.js';
export function parseOptions(name, node, vfile, optionsSpec) {
var _a, _b;
let validationError = false;
// Handle options
const options = {};
// Only look to the direct children here (i.e. for nested roles)
const optionNodes = (_b = (_a = node.children) === null || _a === void 0 ? void 0 : _a.filter((c) => c.type === 'mystOption')) !== null && _b !== void 0 ? _b : [];
const optionNodeLookup = {};
optionNodes.forEach((optionNode) => {
if (optionNode.name === 'id' && optionNode.location === 'inline') {
// The ID maps to the label
optionNodeLookup.label = optionNode;
return;
}
if (optionNode.name === 'class') {
if (optionNodeLookup.class) {
// Combine the option class nodes
// Note: This results in an position error for additional classes
// For example, pointing to a warning will always be the first class.
// That is fine for now.
optionNodeLookup.class.value += ` ${optionNode.value}`;
}
else {
optionNodeLookup.class = optionNode;
}
return;
}
if (optionNodeLookup[optionNode.name]) {
fileWarn(vfile, `duplicate option "${optionNode.name}" declared (in ${name})`, {
node: optionNode,
ruleId: RuleId.directiveOptionsCorrect,
});
}
else {
optionNodeLookup[optionNode.name] = optionNode;
}
});
// Deal with each option in the spec
Object.entries(optionsSpec || {}).forEach(([optionName, optionSpec]) => {
var _a;
let optionNameUsed = optionName;
let optionNode = optionNodeLookup[optionName];
// Replace alias options or warn on duplicates
(_a = optionSpec.alias) === null || _a === void 0 ? void 0 : _a.forEach((alias) => {
const aliasNode = optionNodeLookup[alias];
if (!aliasNode)
return;
if (!optionNode && aliasNode) {
optionNode = aliasNode;
optionNameUsed = alias;
optionNodeLookup[optionName] = optionNode;
}
else {
fileWarn(vfile, `option "${optionNameUsed}" used instead of "${alias}" (in ${name})`, {
node,
ruleId: RuleId.directiveOptionsCorrect,
});
}
delete optionNodeLookup[alias];
});
if (optionSpec.required && !optionNode) {
fileError(vfile, `required option "${optionName}" not provided (in ${name})`, {
node,
ruleId: RuleId.directiveOptionsCorrect,
});
node.type = 'mystDirectiveError';
delete node.children;
validationError = true;
}
else if (optionNode) {
const content = contentFromNode(optionNode, optionSpec, vfile, `option "${optionName}" (in ${name})`, RuleId.directiveOptionsCorrect);
if (content != null) {
options[optionName] = content;
}
else if (optionSpec.required) {
validationError = true;
}
delete optionNodeLookup[optionName];
}
});
Object.values(optionNodeLookup).forEach((optionNode) => {
fileWarn(vfile, `unexpected option "${optionNode.name}" provided (in ${name})`, {
node: optionNode,
ruleId: RuleId.directiveOptionsCorrect,
});
});
return { valid: validationError, options: Object.keys(options).length ? options : undefined };
}