UNPKG

5.21 kBJavaScriptView Raw
1'use strict';
2
3exports.type = 'perItem';
4
5exports.active = false;
6
7exports.params = {
8 delim: '__'
9};
10
11exports.description = 'prefix IDs';
12
13
14var path = require('path'),
15 csstree = require('css-tree'),
16 cssRx = require('css-url-regex'),
17 unquote = require('unquote'),
18 collections = require('./_collections.js'),
19 referencesProps = collections.referencesProps,
20 rxId = /^#(.*)$/, // regular expression for matching an ID + extracing its name
21 addPrefix = null;
22
23
24// Escapes a string for being used as ID
25var escapeIdentifierName = function(str) {
26 return str.replace(/[\. ]/g, '_');
27};
28
29// Matches an #ID value, captures the ID name
30var matchId = function(urlVal) {
31 var idUrlMatches = urlVal.match(rxId);
32 if (idUrlMatches === null) {
33 return false;
34 }
35 return idUrlMatches[1];
36};
37
38// Matches an url(...) value, captures the URL
39var matchUrl = function(val) {
40 var urlMatches = cssRx().exec(val);
41 if (urlMatches === null) {
42 return false;
43 }
44 return urlMatches[1];
45};
46
47// Checks if attribute is empty
48var attrNotEmpty = function(attr) {
49 return (attr && attr.value && attr.value.length > 0);
50};
51
52// prefixes an #ID
53var prefixId = function(val) {
54 var idName = matchId(val);
55 if (!idName) {
56 return false;
57 }
58 return '#' + addPrefix(idName);
59};
60
61
62// attr.value helper methods
63
64// prefixes a class attribute value
65var addPrefixToClassAttr = function(attr) {
66 if (!attrNotEmpty(attr)) {
67 return;
68 }
69
70 attr.value = attr.value.split(/\s+/).map(addPrefix).join(' ');
71};
72
73// prefixes an ID attribute value
74var addPrefixToIdAttr = function(attr) {
75 if (!attrNotEmpty(attr)) {
76 return;
77 }
78
79 attr.value = addPrefix(attr.value);
80};
81
82// prefixes a href attribute value
83var addPrefixToHrefAttr = function(attr) {
84 if (!attrNotEmpty(attr)) {
85 return;
86 }
87
88 var idPrefixed = prefixId(attr.value);
89 if (!idPrefixed) {
90 return;
91 }
92 attr.value = idPrefixed;
93};
94
95// prefixes an URL attribute value
96var addPrefixToUrlAttr = function(attr) {
97 if (!attrNotEmpty(attr)) {
98 return;
99 }
100
101 // url(...) in value
102 var urlVal = matchUrl(attr.value);
103 if (!urlVal) {
104 return;
105 }
106
107 var idPrefixed = prefixId(urlVal);
108 if (!idPrefixed) {
109 return;
110 }
111
112 attr.value = 'url(' + idPrefixed + ')';
113};
114
115
116/**
117 * Prefixes identifiers
118 *
119 * @param {Object} node node
120 * @param {Object} opts plugin params
121 * @param {Object} extra plugin extra information
122 *
123 * @author strarsis <strarsis@gmail.com>
124 */
125exports.fn = function(node, opts, extra) {
126
127 // prefix, from file name or option
128 var prefix = 'prefix';
129 if (opts.prefix) {
130 if (typeof opts.prefix === 'function') {
131 prefix = opts.prefix(node, extra);
132 } else {
133 prefix = opts.prefix;
134 }
135 } else if (opts.prefix === false) {
136 prefix = false;
137 } else if (extra && extra.path && extra.path.length > 0) {
138 var filename = path.basename(extra.path);
139 prefix = filename;
140 }
141
142
143 // prefixes a normal value
144 addPrefix = function(name) {
145 if(prefix === false){
146 return escapeIdentifierName(name);
147 }
148 return escapeIdentifierName(prefix + opts.delim + name);
149 };
150
151
152 // <style/> property values
153
154 if (node.elem === 'style') {
155 if (node.isEmpty()) {
156 // skip empty <style/>s
157 return node;
158 }
159
160 var cssStr = node.content[0].text || node.content[0].cdata || [];
161
162 var cssAst = {};
163 try {
164 cssAst = csstree.parse(cssStr, {
165 parseValue: true,
166 parseCustomProperty: false
167 });
168 } catch (parseError) {
169 console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError);
170 return node;
171 }
172
173 var idPrefixed = '';
174 csstree.walk(cssAst, function(node) {
175
176 // #ID, .class
177 if ((node.type === 'IdSelector' ||
178 node.type === 'ClassSelector') &&
179 node.name) {
180 node.name = addPrefix(node.name);
181 return;
182 }
183
184 // url(...) in value
185 if (node.type === 'Url' &&
186 node.value.value && node.value.value.length > 0) {
187 idPrefixed = prefixId(unquote(node.value.value));
188 if (!idPrefixed) {
189 return;
190 }
191 node.value.value = idPrefixed;
192 }
193
194 });
195
196 // update <style>s
197 node.content[0].text = csstree.generate(cssAst);
198 return node;
199 }
200
201
202 // element attributes
203
204 if (!node.attrs) {
205 return node;
206 }
207
208 // ID
209 addPrefixToIdAttr(node.attrs.id);
210
211 // Class
212 addPrefixToClassAttr(node.attrs.class);
213
214 // href
215 addPrefixToHrefAttr(node.attrs.href);
216
217 // (xlink:)href (deprecated, must be still supported)
218 addPrefixToHrefAttr(node.attrs['xlink:href']);
219
220 // referenceable properties
221 for (var referencesProp of referencesProps) {
222 addPrefixToUrlAttr(node.attrs[referencesProp]);
223 }
224
225
226 return node;
227};