UNPKG

ecmarkup

Version:

Custom element definitions and core utilities for markup that specifies ECMAScript and related technologies.

122 lines (121 loc) 4.66 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = iterator; function iterator(spec) { const ids = []; let inAnnex = false; let currentLevel = 0; return { next(clauseStack, node) { const annex = node.nodeName === 'EMU-ANNEX'; const level = clauseStack.length; if (inAnnex && !annex) { spec.warn({ type: 'node', node, ruleId: 'clause-after-annex', message: 'clauses cannot follow annexes', }); } if (level - currentLevel > 1) { spec.warn({ type: 'node', node, ruleId: 'skipped-caluse', message: 'clause is being numbered without numbering its parent clause', }); } const nextNum = annex ? nextAnnexNum : nextClauseNum; if (level === currentLevel) { ids[currentLevel] = nextNum(clauseStack, node); } else if (level > currentLevel) { ids.push(nextNum(clauseStack, node)); } else { ids.length = level + 1; ids[level] = nextNum(clauseStack, node); } currentLevel = level; return ids.flat().join('.'); }, }; function nextAnnexNum(clauseStack, node) { const level = clauseStack.length; if (level === 0 && node.hasAttribute('number')) { spec.warn({ type: 'attr', node, attr: 'number', ruleId: 'annex-clause-number', message: 'top-level annexes do not support explicit numbers; if you need this, open a bug on ecmarkup', }); } if (!inAnnex) { if (level > 0) { spec.warn({ type: 'node', node, ruleId: 'annex-depth', message: 'first annex must be at depth 0', }); } inAnnex = true; return 'A'; } if (level === 0) { return String.fromCharCode(ids[0].charCodeAt(0) + 1); } return nextClauseNum(clauseStack, node); } function nextClauseNum({ length: level }, node) { if (node.hasAttribute('number')) { const nums = node .getAttribute('number') .split('.') .map(n => Number(n)); if (nums.length === 0 || nums.some(num => !Number.isSafeInteger(num) || num <= 0)) { spec.warn({ type: 'attr-value', node, attr: 'number', ruleId: 'invalid-clause-number', message: 'clause numbers must be positive integers or dotted lists of positive integers', }); } if (ids[level] !== undefined) { if (nums.length !== ids[level].length) { spec.warn({ type: 'attr-value', node, attr: 'number', ruleId: 'invalid-clause-number', message: 'multi-step explicit clause numbers should not be mixed with single-step clause numbers in the same parent clause', }); } else { // Make sure that `nums` is strictly greater than `ids[level]` (i.e., // that their items are not identical and that the item in `nums` is // strictly greater than the value in `ids[level]` at the first // index where they differ). const i = nums.findIndex((num, i) => num !== ids[level][i]); if (i < 0 || !(nums[i] > ids[level][i])) { spec.warn({ type: 'attr-value', node, attr: 'number', ruleId: 'invalid-clause-number', message: 'clause numbers should be strictly increasing', }); } } } return nums; } if (ids[level] === undefined) return [1]; const head = ids[level].slice(0, -1); const tail = ids[level][ids[level].length - 1]; return [...head, tail + 1]; } }