UNPKG

5.66 kBJavaScriptView Raw
1'use strict';
2
3var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
4
5function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
6
7function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
8
9var _ = require('lodash');
10var features = require('../data/features');
11
12var PLUGIN_OPTION_COMMENT = 'doiuse-';
13var DISABLE_FEATURE_COMMENT = PLUGIN_OPTION_COMMENT + 'disable';
14var ENABLE_FEATURE_COMMENT = PLUGIN_OPTION_COMMENT + 'enable';
15
16/*
17 * str: string to search in.
18 * searchfor: string or pattern to search for.
19 */
20function isFoundIn(str) {
21 str = stripUrls(str);
22 return function find(searchfor) {
23 if (searchfor instanceof RegExp) return searchfor.test(str);else if (_.isFunction(searchfor)) return searchfor(str);else return str && str.indexOf(searchfor) >= 0;
24 };
25}
26
27/*
28 * Strip the contents of url literals so they aren't matched
29 * by our naive substring matching.
30 */
31function stripUrls(str) {
32 return str.replace(/url\([^\)]*\)/g, 'url()'); // eslint-disable-line no-useless-escape
33}
34
35/**
36 * Detect the use of any of a given list of CSS features.
37 * ```
38 * var detector = new Detector(featureList)
39 * detector.process(css, cb)
40 * ```
41 *
42 * `featureList`: an array of feature slugs (see caniuse-db)
43 * `cb`: a callback that gets called for each usage of one of the given features,
44 * called with an argument like:
45 * ```
46 * {
47 * usage: {} // postcss node where usage was found
48 * feature: {} // caniuse-db feature slug
49 * ignore: {} // caniuse-db feature to ignore in current file
50 * }
51 * ```
52 */
53module.exports = (function () {
54 function Detector(featureList) {
55 _classCallCheck(this, Detector);
56
57 this.features = _.pick(features, featureList);
58 this.ignore = [];
59 }
60
61 _createClass(Detector, [{
62 key: 'decl',
63 value: function decl(_decl, cb) {
64 for (var feat in this.features) {
65 var properties = this.features[feat].properties || [];
66 var values = this.features[feat].values;
67 if (properties.filter(isFoundIn(_decl.prop)).length > 0) {
68 if (!values || values.filter(isFoundIn(_decl.value)).length > 0) {
69 cb({ usage: _decl, feature: feat, ignore: this.ignore });
70 }
71 }
72 }
73 }
74 }, {
75 key: 'rule',
76 value: function rule(_rule, cb) {
77 for (var feat in this.features) {
78 var selectors = this.features[feat].selectors || [];
79 if (selectors.filter(isFoundIn(_rule.selector)).length > 0) {
80 cb({ usage: _rule, feature: feat, ignore: this.ignore });
81 }
82 }
83
84 this.node(_rule, cb);
85 }
86 }, {
87 key: 'atrule',
88 value: function atrule(_atrule, cb) {
89 for (var feat in this.features) {
90 var atrules = this.features[feat].atrules || [];
91 var params = this.features[feat].params;
92 if (atrules.filter(isFoundIn(_atrule.name)).length > 0) {
93 if (!params || params.filter(isFoundIn(_atrule.params)).length > 0) {
94 cb({ usage: _atrule, feature: feat, ignore: this.ignore });
95 }
96 }
97 }
98
99 this.node(_atrule, cb);
100 }
101 }, {
102 key: 'comment',
103 value: function comment(_comment, cb) {
104 var text = _comment.text.toLowerCase();
105
106 if (_.startsWith(text, PLUGIN_OPTION_COMMENT)) {
107 var option = text.split(' ', 1)[0];
108 var value = text.replace(option, '').trim();
109
110 switch (option) {
111 case DISABLE_FEATURE_COMMENT:
112 if (value === '') {
113 this.ignore = _.keysIn(this.features);
114 } else {
115 this.ignore = _.uniq([].concat(_toConsumableArray(this.ignore), _toConsumableArray(value.split(',').map(function (feat) {
116 return feat.trim();
117 }))));
118 }
119 break;
120 case ENABLE_FEATURE_COMMENT:
121 if (value === '') {
122 this.ignore = [];
123 } else {
124 this.ignore = _.without.apply(_, [this.ignore].concat(_toConsumableArray(value.split(',').map(function (feat) {
125 return feat.trim();
126 }))));
127 }
128 break;
129 }
130 }
131 }
132 }, {
133 key: 'node',
134 value: function node(_node, cb) {
135 var _this = this;
136
137 _node.each(function (child) {
138 switch (child.type) {
139 case 'rule':
140 _this.rule(child, cb);
141 break;
142 case 'decl':
143 _this.decl(child, cb);
144 break;
145 case 'atrule':
146 _this.atrule(child, cb);
147 break;
148 case 'comment':
149 _this.comment(child, cb);
150 break;
151 default:
152 throw new Error('Unkonwn node type ' + child.type);
153 }
154 });
155 }
156 }, {
157 key: 'process',
158 value: function process(node, cb) {
159 // Reset ignoring rules specified by inline comments per each file
160 this.ignore = [];
161
162 // Recursively walk nodes in file
163 this.node(node, cb);
164 }
165 }]);
166
167 return Detector;
168})();
\No newline at end of file