1 |
|
2 |
|
3 | 'use strict';
|
4 |
|
5 | const _ = require('lodash');
|
6 | const declarationValueIndex = require('../../utils/declarationValueIndex');
|
7 | const keywordSets = require('../../reference/keywordSets');
|
8 | const optionsMatches = require('../../utils/optionsMatches');
|
9 | const postcss = require('postcss');
|
10 | const report = require('../../utils/report');
|
11 | const ruleMessages = require('../../utils/ruleMessages');
|
12 | const validateOptions = require('../../utils/validateOptions');
|
13 | const valueParser = require('postcss-value-parser');
|
14 |
|
15 | const ruleName = 'time-min-milliseconds';
|
16 |
|
17 | const messages = ruleMessages(ruleName, {
|
18 | expected: (time) => `Expected a minimum of ${time} milliseconds`,
|
19 | });
|
20 |
|
21 | const DELAY_PROPERTIES = ['animation-delay', 'transition-delay'];
|
22 |
|
23 | function rule(minimum, options) {
|
24 | return (root, result) => {
|
25 | const validOptions = validateOptions(
|
26 | result,
|
27 | ruleName,
|
28 | {
|
29 | actual: minimum,
|
30 | possible: _.isNumber,
|
31 | },
|
32 | {
|
33 | actual: options,
|
34 | possible: {
|
35 | ignore: ['delay'],
|
36 | },
|
37 | optional: true,
|
38 | },
|
39 | );
|
40 |
|
41 | if (!validOptions) {
|
42 | return;
|
43 | }
|
44 |
|
45 | root.walkDecls((decl) => {
|
46 | const propertyName = postcss.vendor.unprefixed(decl.prop.toLowerCase());
|
47 |
|
48 | if (
|
49 | keywordSets.longhandTimeProperties.has(propertyName) &&
|
50 | !isIgnoredProperty(propertyName) &&
|
51 | !isAcceptableTime(decl.value)
|
52 | ) {
|
53 | complain(decl);
|
54 | }
|
55 |
|
56 | if (keywordSets.shorthandTimeProperties.has(propertyName)) {
|
57 | const valueListList = postcss.list.comma(decl.value);
|
58 |
|
59 | for (const valueListString of valueListList) {
|
60 | const valueList = postcss.list.space(valueListString);
|
61 |
|
62 | if (optionsMatches(options, 'ignore', 'delay')) {
|
63 |
|
64 | const duration = getDuration(valueList);
|
65 |
|
66 | if (duration && !isAcceptableTime(duration)) {
|
67 | complain(decl, decl.value.indexOf(duration));
|
68 | }
|
69 | } else {
|
70 |
|
71 | for (const value of valueList) {
|
72 | if (!isAcceptableTime(value)) {
|
73 | complain(decl, decl.value.indexOf(value));
|
74 | }
|
75 | }
|
76 | }
|
77 | }
|
78 | }
|
79 | });
|
80 |
|
81 | |
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 | function getDuration(valueList) {
|
89 | for (const value of valueList) {
|
90 | const parsedTime = valueParser.unit(value);
|
91 |
|
92 | if (!parsedTime) continue;
|
93 |
|
94 |
|
95 | return value;
|
96 | }
|
97 | }
|
98 |
|
99 | function isIgnoredProperty(propertyName) {
|
100 | if (optionsMatches(options, 'ignore', 'delay') && DELAY_PROPERTIES.includes(propertyName)) {
|
101 | return true;
|
102 | }
|
103 |
|
104 | return false;
|
105 | }
|
106 |
|
107 | function isAcceptableTime(time) {
|
108 | const parsedTime = valueParser.unit(time);
|
109 |
|
110 | if (!parsedTime) return true;
|
111 |
|
112 | if (parsedTime.number <= 0) {
|
113 | return true;
|
114 | }
|
115 |
|
116 | if (parsedTime.unit.toLowerCase() === 'ms' && parsedTime.number < minimum) {
|
117 | return false;
|
118 | }
|
119 |
|
120 | if (parsedTime.unit.toLowerCase() === 's' && parsedTime.number * 1000 < minimum) {
|
121 | return false;
|
122 | }
|
123 |
|
124 | return true;
|
125 | }
|
126 |
|
127 | function complain(decl, offset = 0) {
|
128 | report({
|
129 | result,
|
130 | ruleName,
|
131 | message: messages.expected(minimum),
|
132 | index: declarationValueIndex(decl) + offset,
|
133 | node: decl,
|
134 | });
|
135 | }
|
136 | };
|
137 | }
|
138 |
|
139 | rule.ruleName = ruleName;
|
140 | rule.messages = messages;
|
141 | module.exports = rule;
|