Skip to content

Commit e544095

Browse files
committed
Migrated to css-selector-tokenizer
1 parent cdc5ac0 commit e544095

File tree

4 files changed

+47
-119
lines changed

4 files changed

+47
-119
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
44

55
## [Unreleased][unreleased]
66
### Changed
7-
- Nothing yet.
7+
- Migrated to `css-selector-tokenizer`.
88

99
## [0.0.6] - 2015-05-28
1010
### Changed

index.js

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,54 @@
11
var postcss = require('postcss');
2+
var Tokenizer = require('css-selector-tokenizer');
23

3-
var ESCAPED_DOT = ' ___LOCAL_SCOPE__ESCAPED_DOT___ ';
4+
function localizeNodes(nodes) {
5+
var isGlobalContext = false;
46

5-
module.exports = postcss.plugin('postcss-modules-local-by-default', function (config) {
6-
var options = config || {};
7-
return function(css, result) {
8-
css.eachRule(function(rule) {
9-
rule.selector = rule.selector
10-
.split(',')
11-
.map(transformSelector.bind(null, options, rule))
12-
.join(',');
13-
});
14-
};
15-
});
7+
return nodes
8+
.map(function(node, i) {
9+
var newNode = node;
1610

17-
function transformSelector(options, rule, selector) {
18-
var trimmedSelector = selector.trim();
11+
if (isGlobal(newNode)) {
12+
isGlobalContext = true;
13+
return null;
14+
}
15+
16+
if (newNode.type === 'spacing' && isGlobal(nodes[i-1])) {
17+
return null;
18+
}
1919

20-
if (options.lint) {
21-
if (!/^\./.test(trimmedSelector) && !/^\:local/.test(trimmedSelector)) {
22-
if (!/^\:global/.test(trimmedSelector)) {
23-
throw rule.error('Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global '+trimmedSelector+'"', { plugin: 'postcss-modules-local-by-default' });
20+
if (!isGlobalContext && node.type === 'class') {
21+
newNode = { type: 'nested-pseudo-class', name: 'local', nodes: [node] }
22+
} else if (isNestedGlobal(newNode)) {
23+
newNode = node.nodes[0];
24+
} else if (!isNestedLocal(newNode) && newNode.nodes) {
25+
newNode.nodes = localizeNodes(newNode.nodes);
2426
}
25-
}
26-
}
27-
28-
return selector
29-
.replace(/\:global\((.*?)\)/g, escapeDots)
30-
.replace(/\:global (.*)/g, escapeDots)
31-
.replace(/(\:extends\((.*?)\))/g, escapeDots)
32-
.replace(/\.local\[(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)\]/g, '.$1') // source: http://stackoverflow.com/questions/448981/what-characters-are-valid-in-css-class-names-selectors
33-
.replace(/\:local\(\.(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)\)/g, '.$1')
34-
.replace(/\.(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)/g, ':local(.$1)')
35-
.replace(new RegExp(ESCAPED_DOT, 'g'), '.');
27+
28+
return newNode;
29+
}).filter(function(node) {
30+
return node !== null
31+
});
3632
}
3733

38-
function escapeDots(match, p1) {
39-
return p1.replace(/\./g, ESCAPED_DOT);
34+
function isGlobal(node) {
35+
return node.type === 'pseudo-class' && node.name === 'global';
4036
}
37+
38+
function isNestedGlobal(node) {
39+
return node.type === 'nested-pseudo-class' && node.name === 'global';
40+
}
41+
42+
function isNestedLocal(node) {
43+
return node.type === 'nested-pseudo-class' && node.name === 'local';
44+
}
45+
46+
module.exports = postcss.plugin('postcss-modules-local-by-default', function () {
47+
return function(css, result) {
48+
css.eachRule(function(rule) {
49+
var selector = Tokenizer.parse(rule.selector);
50+
selector.nodes = localizeNodes(selector.nodes);
51+
rule.selector = Tokenizer.stringify(selector).trim();
52+
});
53+
};
54+
});

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +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",
1819
"postcss": "^4.1.5"
1920
},
2021
"devDependencies": {

test.js

Lines changed: 0 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -88,41 +88,6 @@ var tests = [
8888
should: 'ignore psuedo elements that are already local',
8989
input: ':local(.foo):after {}',
9090
expected: ':local(.foo):after {}'
91-
},
92-
{
93-
should: 'convert nested selectors that are already local using the old syntax into the new local syntax',
94-
input: '.local[foo] .local[bar] {}',
95-
expected: ':local(.foo) :local(.bar) {}'
96-
},
97-
{
98-
should: 'convert multiple selectors that are already local using the old syntax into the new local syntax',
99-
input: '.local[foo], .local[bar] {}',
100-
expected: ':local(.foo), :local(.bar) {}'
101-
},
102-
{
103-
should: 'convert sibling selectors that are already local using the old syntax into the new local syntax',
104-
input: '.local[foo] ~ .local[bar] {}',
105-
expected: ':local(.foo) ~ :local(.bar) {}'
106-
},
107-
{
108-
should: 'convert psuedo elements that are already local using the old syntax into the new local syntax',
109-
input: '.local[foo]:after {}',
110-
expected: ':local(.foo):after {}'
111-
},
112-
{
113-
should: 'not reject non-global element selectors when lint mode is not enabled',
114-
input: 'input {}',
115-
expected: 'input {}'
116-
},
117-
{
118-
should: 'support :extends',
119-
input: '.foo:extends(.className) {}',
120-
expected: ':local(.foo):extends(.className) {}'
121-
},
122-
{
123-
should: 'support imported :extends',
124-
input: '.foo:extends(.button from "library/button.css") {}',
125-
expected: ':local(.foo):extends(.button from "library/button.css") {}'
12691
}
12792
];
12893

@@ -140,58 +105,6 @@ test(name, function (t) {
140105
});
141106

142107

143-
var errorTests = [
144-
{
145-
should: 'reject non-global element selectors',
146-
input: 'input {}',
147-
reason: 'Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global input"'
148-
},
149-
{
150-
should: 'reject non-global element selectors in a collection',
151-
input: '.foo, input {}',
152-
reason: 'Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global input"'
153-
},
154-
{
155-
should: 'reject non-global psuedo classes',
156-
input: ':focus {}',
157-
reason: 'Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global :focus"'
158-
},
159-
{
160-
should: 'reject non-global psuedo classes in a collection',
161-
input: '.foo, :focus {}',
162-
reason: 'Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global :focus"'
163-
},
164-
{
165-
should: 'reject non-global attribute selectors',
166-
input: '[data-foobar] {}',
167-
reason: 'Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global [data-foobar]"'
168-
},
169-
{
170-
should: 'reject non-global attribute selectors in a collection',
171-
input: '.foo, [data-foobar] {}',
172-
reason: 'Global selector detected in local context. Does this selector really need to be global? If so, you need to explicitly export it into the global scope with ":global", e.g. ":global [data-foobar]"'
173-
}
174-
];
175-
176-
function processError (css, options) {
177-
try {
178-
postcss(plugin(options)).process(css).css;
179-
} catch (error) {
180-
return error;
181-
}
182-
}
183-
184-
test(name, function (t) {
185-
t.plan(errorTests.length);
186-
187-
errorTests.forEach(function (test) {
188-
var options = { lint: true };
189-
var error = processError(test.input, options);
190-
t.equal(error.reason, test.reason, 'should ' + test.should);
191-
});
192-
});
193-
194-
195108
test('should use the postcss plugin api', function (t) {
196109
t.plan(2);
197110
t.ok(plugin().postcssVersion, 'should be able to access version');

0 commit comments

Comments
 (0)