UNPKG

@terrazzo/parser

Version:

Parser/validator for the Design Tokens Community Group (DTCG) standard.

78 lines 3.39 kB
import { isTokenMatch } from '@terrazzo/token-tools'; import { docsLink } from '../lib/docs.js'; export const REQUIRED_CHILDREN = 'core/required-children'; export const ERROR_EMPTY_MATCH = 'EMPTY_MATCH'; export const ERROR_MISSING_REQUIRED_TOKENS = 'MISSING_REQUIRED_TOKENS'; export const ERROR_MISSING_REQUIRED_GROUP = 'MISSING_REQUIRED_GROUP'; const rule = { meta: { messages: { [ERROR_EMPTY_MATCH]: 'No tokens matched {{ matcher }}', [ERROR_MISSING_REQUIRED_TOKENS]: 'Match {{ index }}: some groups missing required token "{{ token }}"', [ERROR_MISSING_REQUIRED_GROUP]: 'Match {{ index }}: some tokens missing required group "{{ group }}"', }, docs: { description: 'Enforce token groups have specific children, whether tokens and/or groups.', url: docsLink(REQUIRED_CHILDREN), }, }, defaultOptions: { matches: [] }, create({ tokens, options, report }) { if (!options.matches?.length) { throw new Error('Invalid config. Missing `matches: […]`'); } // note: in many other rules, the operation can be completed in one iteration through all tokens // in this rule, however, we have to scan all tokens every time per-match, because they may overlap for (let matchI = 0; matchI < options.matches.length; matchI++) { const { match, requiredTokens, requiredGroups } = options.matches[matchI]; // validate if (!match.length) { throw new Error(`Match ${matchI}: must declare \`match: […]\``); } if (!requiredTokens?.length && !requiredGroups?.length) { throw new Error(`Match ${matchI}: must declare either \`requiredTokens: […]\` or \`requiredGroups: […]\``); } const matchGroups = []; const matchTokens = []; let tokensMatched = false; for (const t of Object.values(tokens)) { if (!isTokenMatch(t.id, match)) { continue; } tokensMatched = true; const groups = t.id.split('.'); matchTokens.push(groups.pop()); matchGroups.push(...groups); } if (!tokensMatched) { report({ messageId: ERROR_EMPTY_MATCH, data: { matcher: JSON.stringify(match) }, }); continue; } if (requiredTokens) { for (const id of requiredTokens) { if (!matchTokens.includes(id)) { report({ messageId: ERROR_MISSING_REQUIRED_TOKENS, data: { index: matchI, token: id }, }); } } } if (requiredGroups) { for (const groupName of requiredGroups) { if (!matchGroups.includes(groupName)) { report({ messageId: ERROR_MISSING_REQUIRED_GROUP, data: { index: matchI, group: groupName }, }); } } } } }, }; export default rule; //# sourceMappingURL=required-children.js.map