UNPKG

3.52 kBJavaScriptView Raw
1// @ts-nocheck
2
3'use strict';
4
5const report = require('../../utils/report');
6const ruleMessages = require('../../utils/ruleMessages');
7const validateOptions = require('../../utils/validateOptions');
8
9const ruleName = 'no-irregular-whitespace';
10const messages = ruleMessages(ruleName, {
11 unexpected: 'Unexpected irregular whitespace',
12});
13
14const IRREGULAR_WHITESPACES = [
15 '\u000B', // Line Tabulation (\v) - <VT>
16 '\u000C', // Form Feed (\f) - <FF>
17 '\u00A0', // No-Break Space - <NBSP>
18 '\u0085', // Next Line
19 '\u1680', // Ogham Space Mark
20 '\u180E', // Mongolian Vowel Separator - <MVS>
21 '\uFEFF', // Zero Width No-Break Space - <BOM>
22 '\u2000', // En Quad
23 '\u2001', // Em Quad
24 '\u2002', // En Space - <ENSP>
25 '\u2003', // Em Space - <EMSP>
26 '\u2004', // Tree-Per-Em
27 '\u2005', // Four-Per-Em
28 '\u2006', // Six-Per-Em
29 '\u2007', // Figure Space
30 '\u2008', // Punctuation Space - <PUNCSP>
31 '\u2009', // Thin Space
32 '\u200A', // Hair Space
33 '\u200B', // Zero Width Space - <ZWSP>
34 '\u2028', // Line Separator
35 '\u2029', // Paragraph Separator
36 '\u202F', // Narrow No-Break Space
37 '\u205F', // Medium Mathematical Space
38 '\u3000', // Ideographic Space
39];
40
41const IRREGULAR_WHITESPACES_PATTERN = new RegExp(`([${IRREGULAR_WHITESPACES.join('')}])`);
42
43const generateInvalidWhitespaceValidator = () => {
44 return (str) => typeof str === 'string' && IRREGULAR_WHITESPACES_PATTERN.exec(str);
45};
46
47const declarationSchema = {
48 prop: 'string',
49 value: 'string',
50 raws: {
51 before: 'string',
52 between: 'string',
53 },
54};
55
56const atRuleSchema = {
57 name: 'string',
58 params: 'string',
59 raws: {
60 before: 'string',
61 between: 'string',
62 afterName: 'string',
63 after: 'string',
64 },
65};
66
67const ruleSchema = {
68 selector: 'string',
69 raws: {
70 before: 'string',
71 between: 'string',
72 after: 'string',
73 },
74};
75
76const generateNodeValidator = (nodeSchema, validator) => {
77 const allKeys = Object.keys(nodeSchema);
78 const validatorForKey = {};
79
80 allKeys.forEach((key) => {
81 if (typeof nodeSchema[key] === 'string') validatorForKey[key] = validator;
82
83 if (typeof nodeSchema[key] === 'object')
84 validatorForKey[key] = generateNodeValidator(nodeSchema[key], validator);
85 });
86
87 // This will be called many times, so it's optimized for performance and not readibility.
88 // Surprisingly, this seem to be slightly faster then concatenating the params and running the validator once.
89 return (node) => {
90 for (const currentKey of allKeys) {
91 if (validatorForKey[currentKey](node[currentKey])) {
92 return validatorForKey[currentKey](node[currentKey]);
93 }
94 }
95 };
96};
97
98function rule(on) {
99 return (root, result) => {
100 const validOptions = validateOptions(result, ruleName, { actual: on });
101
102 if (!validOptions) {
103 return;
104 }
105
106 const genericValidator = generateInvalidWhitespaceValidator();
107
108 const validate = (node, validator) => {
109 const issue = validator(node);
110
111 if (issue) {
112 report({
113 ruleName,
114 result,
115 message: messages.unexpected,
116 node,
117 word: issue[1],
118 });
119 }
120 };
121
122 const atRuleValidator = generateNodeValidator(atRuleSchema, genericValidator);
123 const ruleValidator = generateNodeValidator(ruleSchema, genericValidator);
124 const declValidator = generateNodeValidator(declarationSchema, genericValidator);
125
126 root.walkAtRules((atRule) => validate(atRule, atRuleValidator));
127 root.walkRules((selector) => validate(selector, ruleValidator));
128 root.walkDecls((declaration) => validate(declaration, declValidator));
129 };
130}
131
132rule.ruleName = ruleName;
133rule.messages = messages;
134module.exports = rule;