-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathindex.js
136 lines (110 loc) · 3.96 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
const { createPlugin } = require('stylelint')
const valueParser = require('postcss-value-parser')
const atRuleParamIndex = require('stylelint/lib/utils/atRuleParamIndex')
const declarationValueIndex = require('stylelint/lib/utils/declarationValueIndex')
const report = require('stylelint/lib/utils/report')
const ruleMessages = require('stylelint/lib/utils/ruleMessages')
const { isAtRule } = require('stylelint/lib/utils/typeGuards')
const validateOptions = require('stylelint/lib/utils/validateOptions')
const ruleName = 'stylistic/number-no-trailing-zeros'
const messages = ruleMessages(ruleName, {
rejected: 'Unexpected trailing zero(s)',
})
const meta = {
url: 'https://github.com/elirasza/stylelint-stylistic/tree/main/lib/rules/number-no-trailing-zeros',
fixable: true,
}
/** @type {import('stylelint').Rule} */
const rule = (primary, _secondaryOptions, context) => (root, result) => {
const validOptions = validateOptions(result, ruleName, { actual: primary })
if (!validOptions) {
return
}
root.walkAtRules((atRule) => {
if (atRule.name.toLowerCase() === 'import') {
return
}
check(atRule, atRule.params)
})
root.walkDecls((decl) => check(decl, decl.value))
/**
* @param {import('postcss').AtRule | import('postcss').Declaration} node
* @param {string} value
*/
function check(node, value) {
/** @type {Array<{ startIndex: number, endIndex: number }>} */
const fixPositions = []
// Get out quickly if there are no periods
if (!value.includes('.')) {
return
}
valueParser(value).walk((valueNode) => {
// Ignore `url` function
if (valueNode.type === 'function' && valueNode.value.toLowerCase() === 'url') {
return false
}
// Ignore strings, comments, etc
if (valueNode.type !== 'word') {
return
}
const match = /\.(\d{0,100}?)(0+)(?:\D|$)/.exec(valueNode.value)
// match[1] is any numbers between the decimal and our trailing zero, could be empty
// match[2] is our trailing zero(s)
if (match == null || match[1] == null || match[2] == null) {
return
}
// our index is:
// the index of our valueNode +
// the index of our match +
// 1 for our decimal +
// the length of our potential non-zero number match (match[1])
const index = valueNode.sourceIndex + match.index + 1 + match[1].length
// our startIndex is identical to our index except when we have only
// trailing zeros after our decimal. in that case we don't need the decimal
// either so we move our index back by 1.
const startIndex = match[1].length > 0 ? index : index - 1
// our end index is our original index + the length of our trailing zeros
const endIndex = index + match[2].length
if (context.fix) {
fixPositions.unshift({
startIndex,
endIndex,
})
return
}
const baseIndex = isAtRule(node) ? atRuleParamIndex(node) : declarationValueIndex(node)
report({
message: messages.rejected,
node,
// this is the index of the _first_ trailing zero
index: baseIndex + index,
result,
ruleName,
})
})
if (fixPositions.length) {
for (const fixPosition of fixPositions) {
const { startIndex } = fixPosition
const { endIndex } = fixPosition
if (isAtRule(node)) {
node.params = removeTrailingZeros(node.params, startIndex, endIndex)
} else {
node.value = removeTrailingZeros(node.value, startIndex, endIndex)
}
}
}
}
}
/**
* @param {string} input
* @param {number} startIndex
* @param {number} endIndex
* @returns {string}
*/
function removeTrailingZeros(input, startIndex, endIndex) {
return input.slice(0, startIndex) + input.slice(endIndex)
}
rule.ruleName = ruleName
rule.messages = messages
rule.meta = meta
module.exports = { messages, meta, plugin: createPlugin(ruleName, rule), rule, ruleName }