Skip to content

Commit 7765bc3

Browse files
cmrnclshortfuse
authored andcommitted
refactor Detector
1 parent efa8d04 commit 7765bc3

File tree

1 file changed

+48
-34
lines changed

1 file changed

+48
-34
lines changed

lib/Detector.js

Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import FEATURES from '../data/features.js';
22
import { performFeatureCheck, stripUrls } from '../utils/util.js';
33

4+
/** @typedef {import('../data/features.js').FeatureKeys} FeatureKeys */
5+
/** @typedef {import('../data/features.js').RuleCheck} RuleCheck */
6+
47
/**
58
* @typedef DetectorCallbackArgument
69
* @prop {!import('postcss').ChildNode} usage
7-
* @prop {keyof FEATURES} feature
8-
* @prop {(keyof FEATURES & string)[]} ignore
10+
* @prop {FeatureKeys} feature
11+
* @prop {(FeatureKeys & string)[]} ignore
912
*/
1013

1114
/**
@@ -18,6 +21,35 @@ const PLUGIN_OPTION_COMMENT = 'doiuse-';
1821
const DISABLE_FEATURE_COMMENT = `${PLUGIN_OPTION_COMMENT}disable`;
1922
const ENABLE_FEATURE_COMMENT = `${PLUGIN_OPTION_COMMENT}enable`;
2023

24+
/**
25+
* Normalise a Feature into a RuleCheck function.
26+
* @param {import('../data/features.js').Feature} feature
27+
* @return {RuleCheck}
28+
*/
29+
function normaliseFeature(feature) {
30+
if (typeof feature === 'function') {
31+
return feature;
32+
}
33+
if (Array.isArray(feature)) {
34+
return (child) => feature.some((function_) => function_(child));
35+
}
36+
if (typeof feature === 'object') {
37+
const properties = Object.entries(feature);
38+
return (child) => {
39+
if (child.type !== 'decl') {
40+
return false;
41+
}
42+
return properties.some(([property, value]) => {
43+
if (property !== '' && property !== child.prop) return false;
44+
if (value === true) return true;
45+
if (value === false) return false;
46+
return performFeatureCheck(value, stripUrls(child.value));
47+
});
48+
};
49+
}
50+
throw new TypeError(`Invalid feature definition: ${feature}`);
51+
}
52+
2153
/**
2254
* Detect the use of any of a given list of CSS features.
2355
* ```
@@ -38,17 +70,17 @@ const ENABLE_FEATURE_COMMENT = `${PLUGIN_OPTION_COMMENT}enable`;
3870
*/
3971
export default class Detector {
4072
/**
41-
* @param {(keyof FEATURES & string)[]} featureList an array of feature slugs (see caniuse-db)
73+
* @param {(FeatureKeys & string)[]} featureList an array of feature slugs (see caniuse-db)
4274
*/
4375
constructor(featureList) {
44-
/** @type {Partial<FEATURES>} */
45-
this.features = {};
46-
for (const feature of featureList) {
47-
if (FEATURES[feature]) {
48-
this.features[feature] = FEATURES[feature];
49-
}
50-
}
51-
/** @type {(keyof FEATURES & string)[]} */
76+
/** @type {[FeatureKeys, RuleCheck][]} */
77+
this.features = featureList
78+
.filter((featureName) => FEATURES[featureName] != null)
79+
.map((featureName) => {
80+
const feature = FEATURES[featureName];
81+
return [featureName, normaliseFeature(feature)];
82+
});
83+
/** @type {(FeatureKeys & string)[]} */
5284
this.ignore = [];
5385
}
5486

@@ -66,8 +98,7 @@ export default class Detector {
6698
switch (option) {
6799
case DISABLE_FEATURE_COMMENT: {
68100
if (value === '') {
69-
// @ts-expect-error Skip cast
70-
this.ignore = Object.keys(this.features);
101+
this.ignore = this.features.map(([featureName]) => featureName);
71102
} else {
72103
for (const feat of value.split(',')) {
73104
/** @type {any} */
@@ -104,28 +135,11 @@ export default class Detector {
104135
return;
105136
}
106137

107-
for (const [feat] of Object.entries(this.features).filter(([, featValue]) => {
108-
if (!featValue) return false;
109-
if (typeof featValue === 'function') {
110-
return featValue(child);
111-
}
112-
if (Array.isArray(featValue)) {
113-
return featValue.some((function_) => function_(child));
114-
}
115-
if (child.type !== 'decl') {
116-
return false;
117-
}
118-
119-
return Object.entries(featValue).some(([property, value]) => {
120-
if (property !== '' && property !== child.prop) return false;
121-
if (value === true) return true;
122-
if (value === false) return false;
123-
return performFeatureCheck(value, stripUrls(child.value));
124-
});
125-
})) {
126-
const feature = /** @type {keyof FEATURES} */ (feat);
127-
callback({ usage: child, feature, ignore: this.ignore });
138+
const detectedFeatures = this.features.filter(([, ruleCheck]) => ruleCheck(child));
139+
for (const [featureName] of detectedFeatures) {
140+
callback({ usage: child, feature: featureName, ignore: this.ignore });
128141
}
142+
129143
if (child.type !== 'decl') {
130144
this.node(child, callback);
131145
}

0 commit comments

Comments
 (0)