| // @ts-nocheck |
| |
| 'use strict'; |
| |
| const report = require('../../utils/report'); |
| const ruleMessages = require('../../utils/ruleMessages'); |
| const validateOptions = require('../../utils/validateOptions'); |
| |
| const ruleName = 'no-irregular-whitespace'; |
| const messages = ruleMessages(ruleName, { |
| unexpected: 'Unexpected irregular whitespace', |
| }); |
| |
| const IRREGULAR_WHITESPACES = [ |
| '\u000B', // Line Tabulation (\v) - <VT> |
| '\u000C', // Form Feed (\f) - <FF> |
| '\u00A0', // No-Break Space - <NBSP> |
| '\u0085', // Next Line |
| '\u1680', // Ogham Space Mark |
| '\u180E', // Mongolian Vowel Separator - <MVS> |
| '\uFEFF', // Zero Width No-Break Space - <BOM> |
| '\u2000', // En Quad |
| '\u2001', // Em Quad |
| '\u2002', // En Space - <ENSP> |
| '\u2003', // Em Space - <EMSP> |
| '\u2004', // Tree-Per-Em |
| '\u2005', // Four-Per-Em |
| '\u2006', // Six-Per-Em |
| '\u2007', // Figure Space |
| '\u2008', // Punctuation Space - <PUNCSP> |
| '\u2009', // Thin Space |
| '\u200A', // Hair Space |
| '\u200B', // Zero Width Space - <ZWSP> |
| '\u2028', // Line Separator |
| '\u2029', // Paragraph Separator |
| '\u202F', // Narrow No-Break Space |
| '\u205F', // Medium Mathematical Space |
| '\u3000', // Ideographic Space |
| ]; |
| |
| const IRREGULAR_WHITESPACES_PATTERN = new RegExp(`([${IRREGULAR_WHITESPACES.join('')}])`); |
| |
| const generateInvalidWhitespaceValidator = () => { |
| return (str) => typeof str === 'string' && IRREGULAR_WHITESPACES_PATTERN.exec(str); |
| }; |
| |
| const declarationSchema = { |
| prop: 'string', |
| value: 'string', |
| raws: { |
| before: 'string', |
| between: 'string', |
| }, |
| }; |
| |
| const atRuleSchema = { |
| name: 'string', |
| params: 'string', |
| raws: { |
| before: 'string', |
| between: 'string', |
| afterName: 'string', |
| after: 'string', |
| }, |
| }; |
| |
| const ruleSchema = { |
| selector: 'string', |
| raws: { |
| before: 'string', |
| between: 'string', |
| after: 'string', |
| }, |
| }; |
| |
| const generateNodeValidator = (nodeSchema, validator) => { |
| const allKeys = Object.keys(nodeSchema); |
| const validatorForKey = {}; |
| |
| for (const key of allKeys) { |
| if (typeof nodeSchema[key] === 'string') validatorForKey[key] = validator; |
| |
| if (typeof nodeSchema[key] === 'object') |
| validatorForKey[key] = generateNodeValidator(nodeSchema[key], validator); |
| } |
| |
| // This will be called many times, so it's optimized for performance and not readibility. |
| // Surprisingly, this seem to be slightly faster then concatenating the params and running the validator once. |
| return (node) => { |
| for (const currentKey of allKeys) { |
| if (validatorForKey[currentKey](node[currentKey])) { |
| return validatorForKey[currentKey](node[currentKey]); |
| } |
| } |
| }; |
| }; |
| |
| function rule(on) { |
| return (root, result) => { |
| const validOptions = validateOptions(result, ruleName, { actual: on }); |
| |
| if (!validOptions) { |
| return; |
| } |
| |
| const genericValidator = generateInvalidWhitespaceValidator(); |
| |
| const validate = (node, validator) => { |
| const issue = validator(node); |
| |
| if (issue) { |
| report({ |
| ruleName, |
| result, |
| message: messages.unexpected, |
| node, |
| word: issue[1], |
| }); |
| } |
| }; |
| |
| const atRuleValidator = generateNodeValidator(atRuleSchema, genericValidator); |
| const ruleValidator = generateNodeValidator(ruleSchema, genericValidator); |
| const declValidator = generateNodeValidator(declarationSchema, genericValidator); |
| |
| root.walkAtRules((atRule) => validate(atRule, atRuleValidator)); |
| root.walkRules((selector) => validate(selector, ruleValidator)); |
| root.walkDecls((declaration) => validate(declaration, declValidator)); |
| }; |
| } |
| |
| rule.ruleName = ruleName; |
| rule.messages = messages; |
| module.exports = rule; |