Skip to content

Commit cfd8251

Browse files
committed
Merge pull request #12 from css-modules/urls
better parsing for decl values
2 parents 7b6f630 + f89e506 commit cfd8251

File tree

3 files changed

+81
-11
lines changed

3 files changed

+81
-11
lines changed

index.js

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,49 @@ function localizeNode(node, context) {
160160
return node;
161161
}
162162

163-
function localizeDecl(decl) {
164-
if(typeof decl.prop === "string" && /animation(-name)?/.test(decl.prop)) {
165-
decl.value = decl.value.replace(/(^|,)(\s*)(\w+)/g, "$1$2:local($3)"); // TODO
163+
function localizeDeclNode(node, context) {
164+
var newNode;
165+
switch(node.type) {
166+
case "item":
167+
if(context.localizeNextItem) {
168+
newNode = Object.create(node);
169+
newNode.name = ":local(" + newNode.name + ")";
170+
context.localizeNextItem = false;
171+
return newNode;
172+
}
173+
break;
174+
case "url":
175+
if(context.options.rewriteUrl) {
176+
newNode = Object.create(node);
177+
newNode.url = context.options.rewriteUrl(context.global, node.url);
178+
return newNode;
179+
}
180+
break;
166181
}
182+
return node;
183+
}
184+
185+
function localizeDeclValue(valueNode, context) {
186+
var newValueNode = Object.create(valueNode);
187+
newValueNode.nodes = valueNode.nodes.map(function(node) {
188+
return localizeDeclNode(node, context);
189+
});
190+
return newValueNode;
191+
}
192+
193+
function localizeDecl(decl, context) {
194+
var valuesNode = Tokenizer.parseValues(decl.value);
195+
var localizeName = /animation(-name)?/.test(decl.prop);
196+
var newValuesNode = Object.create(valuesNode);
197+
newValuesNode.nodes = valuesNode.nodes.map(function(valueNode) {
198+
var subContext = {
199+
options: context.options,
200+
global: context.global,
201+
localizeNextItem: localizeName && !context.global
202+
};
203+
return localizeDeclValue(valueNode, subContext);
204+
});
205+
decl.value = Tokenizer.stringifyValues(newValuesNode);
167206
}
168207

169208
module.exports = postcss.plugin('postcss-modules-local-by-default', function (options) {
@@ -181,7 +220,7 @@ module.exports = postcss.plugin('postcss-modules-local-by-default', function (op
181220
var localMatch = /^\s*:local\s*\((.+)\)\s*$/.exec(atrule.params);
182221
if(globalMatch) {
183222
if(pureMode) {
184-
throw new Error("@keyframes :global(...) is not allowed in pure mode");
223+
throw atrule.error("@keyframes :global(...) is not allowed in pure mode");
185224
}
186225
atrule.params = globalMatch[1];
187226
} else if(localMatch) {
@@ -194,22 +233,28 @@ module.exports = postcss.plugin('postcss-modules-local-by-default', function (op
194233
css.eachRule(function(rule) {
195234
var selector = Tokenizer.parse(rule.selector);
196235
var context = {
236+
options: options,
197237
global: globalMode,
198238
hasPureGlobals: false,
199239
hasPureImplicitGlobals: false
200240
};
201-
var newSelector = localizeNode(selector, context);
241+
var newSelector;
242+
try {
243+
newSelector = localizeNode(selector, context);
244+
} catch(e) {
245+
throw rule.error(e.message);
246+
}
202247
if(pureMode && context.hasPureGlobals) {
203-
throw new Error("Selector '" + Tokenizer.stringify(selector) + "' is not pure " +
248+
throw rule.error("Selector '" + Tokenizer.stringify(selector) + "' is not pure " +
204249
"(pure selectors must contain at least one local class or id)");
205250
}
206251
if(!globalMode && context.hasPureImplicitGlobals) {
207-
throw new Error("Selector '" + Tokenizer.stringify(selector) + "' must be explicit flagged :global " +
252+
throw rule.error("Selector '" + Tokenizer.stringify(selector) + "' must be explicit flagged :global " +
208253
"(elsewise it would leak globally)");
209254
}
210-
if(!context.global) {
211-
rule.nodes.forEach(localizeDecl);
212-
}
255+
rule.nodes.forEach(function(decl) {
256+
localizeDecl(decl, context);
257+
});
213258
rule.selector = Tokenizer.stringify(newSelector);
214259
});
215260
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"url": "https://github.com/css-modules/postcss-modules-local-by-default.git"
1616
},
1717
"dependencies": {
18-
"css-selector-tokenizer": "^0.3.1",
18+
"css-selector-tokenizer": "^0.4.0",
1919
"postcss": "^4.1.5"
2020
},
2121
"devDependencies": {

test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,31 @@ var tests = [
353353
should: 'throw on implicit global attribute in nested',
354354
input: ':not([type="radio"]) {}',
355355
error: /':not\(\[type="radio"\]\)' must be explicit flagged :global/
356+
},
357+
358+
{
359+
should: 'not modify urls without option',
360+
input: '.a { background: url(./image.png); }\n' +
361+
':global .b { background: url(image.png); }\n' +
362+
'.c { background: url("./image.png"); }',
363+
expected: ':local(.a) { background: url(./image.png); }\n' +
364+
'.b { background: url(image.png); }\n' +
365+
':local(.c) { background: url("./image.png"); }'
366+
},
367+
{
368+
should: 'rewrite url in local block',
369+
input: '.a { background: url(./image.png); }\n' +
370+
':global .b { background: url(image.png); }\n' +
371+
'.c { background: url("./image.png"); }',
372+
options: {
373+
rewriteUrl: function(global, url) {
374+
var mode = global ? 'global' : 'local';
375+
return '(' + mode + ')' + url + '"' + mode + '"';
376+
}
377+
},
378+
expected: ':local(.a) { background: url((local\\)./image.png\\\"local\\\"); }\n' +
379+
'.b { background: url((global\\)image.png\\\"global\\\"); }\n' +
380+
':local(.c) { background: url(\"(local)./image.png\\\"local\\\"\"); }'
356381
}
357382
];
358383

0 commit comments

Comments
 (0)