Skip to content

Commit 53bd7f1

Browse files
jquenseevilebottnawi
authored andcommitted
chore: migrate to postcss-selector-parser (css-modules#5)
1 parent cb28ea5 commit 53bd7f1

File tree

6 files changed

+108
-104
lines changed

6 files changed

+108
-104
lines changed

package.json

+9-5
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,21 @@
3434
"url": "https://github.com/css-modules/postcss-modules-scope/issues"
3535
},
3636
"homepage": "https://github.com/css-modules/postcss-modules-scope",
37+
"prettier": {
38+
"semi": true,
39+
"singleQuote": true,
40+
"trailingComma": "es5"
41+
},
3742
"dependencies": {
38-
"css-selector-tokenizer": "^0.7.0",
39-
"postcss": "^7.0.6"
43+
"postcss": "^7.0.6",
44+
"postcss-selector-parser": "^5.0.0"
4045
},
4146
"devDependencies": {
4247
"chokidar-cli": "^1.0.1",
4348
"codecov.io": "^0.1.2",
4449
"coveralls": "^3.0.2",
45-
"css-selector-parser": "^1.0.4",
4650
"eslint": "^5.9.0",
47-
"nyc": "^13.1.0",
48-
"mocha": "^5.2.0"
51+
"mocha": "^5.2.0",
52+
"nyc": "^13.1.0"
4953
}
5054
}

src/index.js

+45-40
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,53 @@
11
'use strict';
22

33
const postcss = require('postcss');
4-
const Tokenizer = require('css-selector-tokenizer');
4+
const selectorParser = require('postcss-selector-parser');
55

66
const hasOwnProperty = Object.prototype.hasOwnProperty;
77

8-
function getSingleLocalNamesForComposes(selectors) {
9-
return selectors.nodes.map(node => {
8+
function getSingleLocalNamesForComposes(root) {
9+
return root.nodes.map(node => {
1010
if (node.type !== 'selector' || node.nodes.length !== 1) {
1111
throw new Error(
12-
'composition is only allowed when selector is single :local class name not in "' +
13-
Tokenizer.stringify(selectors) +
14-
'"'
12+
`composition is only allowed when selector is single :local class name not in "${root}"`
1513
);
1614
}
1715
node = node.nodes[0];
1816
if (
19-
node.type !== 'nested-pseudo-class' ||
20-
node.name !== 'local' ||
17+
node.type !== 'pseudo' ||
18+
node.value !== ':local' ||
2119
node.nodes.length !== 1
2220
) {
2321
throw new Error(
2422
'composition is only allowed when selector is single :local class name not in "' +
25-
Tokenizer.stringify(selectors) +
23+
root +
2624
'", "' +
27-
Tokenizer.stringify(node) +
25+
node +
2826
'" is weird'
2927
);
3028
}
31-
node = node.nodes[0];
32-
if (node.type !== 'selector' || node.nodes.length !== 1) {
29+
node = node.first;
30+
if (node.type !== 'selector' || node.length !== 1) {
3331
throw new Error(
3432
'composition is only allowed when selector is single :local class name not in "' +
35-
Tokenizer.stringify(selectors) +
33+
root +
3634
'", "' +
37-
Tokenizer.stringify(node) +
35+
node +
3836
'" is weird'
3937
);
4038
}
41-
node = node.nodes[0];
39+
node = node.first;
4240
if (node.type !== 'class') {
4341
// 'id' is not possible, because you can't compose ids
4442
throw new Error(
4543
'composition is only allowed when selector is single :local class name not in "' +
46-
Tokenizer.stringify(selectors) +
44+
root +
4745
'", "' +
48-
Tokenizer.stringify(node) +
46+
node +
4947
'" is weird'
5048
);
5149
}
52-
return node.name;
50+
return node.value;
5351
});
5452
}
5553

@@ -74,40 +72,45 @@ const processor = postcss.plugin('postcss-modules-scope', function(options) {
7472
}
7573

7674
function localizeNode(node) {
77-
const newNode = Object.create(node);
7875
switch (node.type) {
7976
case 'selector':
80-
newNode.nodes = node.nodes.map(localizeNode);
81-
return newNode;
77+
node.nodes = node.map(localizeNode);
78+
return node;
8279
case 'class':
80+
return selectorParser.className({
81+
value: exportScopedName(node.value),
82+
});
8383
case 'id': {
84-
newNode.name = exportScopedName(node.name);
85-
return newNode;
84+
return selectorParser.id({
85+
value: exportScopedName(node.value),
86+
});
8687
}
8788
}
8889
throw new Error(
89-
node.type +
90-
' ("' +
91-
Tokenizer.stringify(node) +
92-
'") is not allowed in a :local block'
90+
`${node.type} ("${node}") is not allowed in a :local block`
9391
);
9492
}
9593

9694
function traverseNode(node) {
9795
switch (node.type) {
98-
case 'nested-pseudo-class':
99-
if (node.name === 'local') {
96+
case 'pseudo':
97+
if (node.value === ':local') {
10098
if (node.nodes.length !== 1) {
10199
throw new Error('Unexpected comma (",") in :local block');
102100
}
103-
return localizeNode(node.nodes[0]);
101+
const selector = localizeNode(node.first, node.spaces);
102+
// move the spaces that were around the psuedo selector to the first
103+
// non-container node
104+
selector.first.spaces = node.spaces;
105+
106+
node.replaceWith(selector);
107+
return;
104108
}
105109
/* falls through */
106-
case 'selectors':
110+
case 'root':
107111
case 'selector': {
108-
const newNode = Object.create(node);
109-
newNode.nodes = node.nodes.map(traverseNode);
110-
return newNode;
112+
node.each(traverseNode);
113+
break;
111114
}
112115
}
113116
return node;
@@ -125,14 +128,16 @@ const processor = postcss.plugin('postcss-modules-scope', function(options) {
125128

126129
// Find any :local classes
127130
css.walkRules(rule => {
128-
const selector = Tokenizer.parse(rule.selector);
129-
const newSelector = traverseNode(selector);
130-
rule.selector = Tokenizer.stringify(newSelector);
131+
let parsedSelector = selectorParser().astSync(rule);
132+
133+
rule.selector = traverseNode(parsedSelector.clone()).toString();
134+
// console.log(rule.selector);
131135
rule.walkDecls(/composes|compose-with/, decl => {
132-
const localNames = getSingleLocalNamesForComposes(selector);
136+
const localNames = getSingleLocalNamesForComposes(parsedSelector);
133137
const classes = decl.value.split(/\s+/);
134138
classes.forEach(className => {
135139
const global = /^global\(([^\)]+)\)$/.exec(className);
140+
136141
if (global) {
137142
localNames.forEach(exportedName => {
138143
exports[exportedName].push(global[1]);
@@ -196,7 +201,7 @@ const processor = postcss.plugin('postcss-modules-scope', function(options) {
196201
exportRule.append({
197202
prop: exportedName,
198203
value: exports[exportedName].join(' '),
199-
raws: { before: '\n ' }
204+
raws: { before: '\n ' },
200205
})
201206
);
202207
css.append(exportRule);
@@ -209,7 +214,7 @@ processor.generateScopedName = function(exportedName, path) {
209214
.replace(/\.[^\.\/\\]+$/, '')
210215
.replace(/[\W_]+/g, '_')
211216
.replace(/^_|_$/g, '');
212-
return `_${sanitisedPath}__${exportedName}`;
217+
return `_${sanitisedPath}__${exportedName}`.trim();
213218
};
214219

215220
module.exports = processor;

test/test-cases.js

+28-11
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,48 @@ var generateScopedName = processor.generateScopedName;
2323
describe('test-cases', function() {
2424
var testDir = path.join(__dirname, 'test-cases');
2525
fs.readdirSync(testDir).forEach(function(testCase) {
26-
if(fs.existsSync(path.join(testDir, testCase, 'source.css'))) {
26+
if (fs.existsSync(path.join(testDir, testCase, 'source.css'))) {
2727
it('should ' + testCase.replace(/-/g, ' '), function() {
28-
var input = normalize(fs.readFileSync(path.join(testDir, testCase, 'source.css'), 'utf-8'));
28+
var input = normalize(
29+
fs.readFileSync(path.join(testDir, testCase, 'source.css'), 'utf-8')
30+
);
31+
2932
var expected, expectedError;
30-
if(fs.existsSync(path.join(testDir, testCase, 'expected.error.txt'))) {
31-
expectedError = normalize(fs.readFileSync(path.join(testDir, testCase, 'expected.error.txt'), 'utf-8'))
32-
.split('\n')[0];
33+
if (fs.existsSync(path.join(testDir, testCase, 'expected.error.txt'))) {
34+
expectedError = normalize(
35+
fs.readFileSync(
36+
path.join(testDir, testCase, 'expected.error.txt'),
37+
'utf-8'
38+
)
39+
).split('\n')[0];
3340
} else {
34-
expected = normalize(fs.readFileSync(path.join(testDir, testCase, 'expected.css'), 'utf-8'));
41+
expected = normalize(
42+
fs.readFileSync(
43+
path.join(testDir, testCase, 'expected.css'),
44+
'utf-8'
45+
)
46+
);
3547
}
3648
var config = { from: '/input' };
3749
var options = {
3850
generateScopedName: function(exportedName, inputPath) {
3951
var normalizedPath = inputPath.replace(/^[A-Z]:/, '');
4052
return generateScopedName(exportedName, normalizedPath);
41-
}
53+
},
4254
};
43-
if(fs.existsSync(path.join(testDir, testCase, 'config.json'))) {
44-
config = JSON.parse(fs.readFileSync(path.join(testDir, testCase, 'config.json'), 'utf-8'));
55+
if (fs.existsSync(path.join(testDir, testCase, 'config.json'))) {
56+
config = JSON.parse(
57+
fs.readFileSync(
58+
path.join(testDir, testCase, 'config.json'),
59+
'utf-8'
60+
)
61+
);
4562
}
46-
if(fs.existsSync(path.join(testDir, testCase, 'options.js'))) {
63+
if (fs.existsSync(path.join(testDir, testCase, 'options.js'))) {
4764
options = require(path.join(testDir, testCase, 'options.js'));
4865
}
4966
var pipeline = postcss([generateInvalidCSS, processor(options)]);
50-
if(expectedError) {
67+
if (expectedError) {
5168
assert.throws(function() {
5269
// eslint-ignore-next-line no-unused-vars
5370
const result = pipeline.process(input, config).css;
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
element \("body"\) is not allowed in a :local block
1+
tag \("body"\) is not allowed in a :local block
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module.exports = {
22
generateScopedName: function(name, path) {
3-
return '_' + name + '_';
4-
}
3+
return '_' + name + '_';
4+
},
55
};

yarn.lock

+23-45
Original file line numberDiff line numberDiff line change
@@ -541,21 +541,10 @@ cryptiles@0.2.x:
541541
dependencies:
542542
boom "0.4.x"
543543

544-
css-selector-parser@^1.0.4:
545-
version "1.3.0"
546-
resolved "https://registry.yarnpkg.com/css-selector-parser/-/css-selector-parser-1.3.0.tgz#5f1ad43e2d8eefbfdc304fcd39a521664943e3eb"
547-
548-
css-selector-tokenizer@^0.7.0:
549-
version "0.7.1"
550-
resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d"
551-
dependencies:
552-
cssesc "^0.1.0"
553-
fastparse "^1.1.1"
554-
regexpu-core "^1.0.0"
555-
556-
cssesc@^0.1.0:
557-
version "0.1.0"
558-
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
544+
cssesc@^2.0.0:
545+
version "2.0.0"
546+
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
547+
integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==
559548

560549
ctype@0.5.3:
561550
version "0.5.3"
@@ -873,10 +862,6 @@ fast-levenshtein@~2.0.4:
873862
version "2.0.6"
874863
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
875864

876-
fastparse@^1.1.1:
877-
version "1.1.2"
878-
resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
879-
880865
figures@^2.0.0:
881866
version "2.0.0"
882867
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
@@ -1175,6 +1160,11 @@ imurmurhash@^0.1.4:
11751160
version "0.1.4"
11761161
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
11771162

1163+
indexes-of@^1.0.1:
1164+
version "1.0.1"
1165+
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
1166+
integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
1167+
11781168
inflight@^1.0.4:
11791169
version "1.0.6"
11801170
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -1431,10 +1421,6 @@ jsesc@^2.5.1:
14311421
version "2.5.2"
14321422
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
14331423

1434-
jsesc@~0.5.0:
1435-
version "0.5.0"
1436-
resolved "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
1437-
14381424
json-parse-better-errors@^1.0.1:
14391425
version "1.0.2"
14401426
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -2031,6 +2017,15 @@ posix-character-classes@^0.1.0:
20312017
version "0.1.1"
20322018
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
20332019

2020+
postcss-selector-parser@^5.0.0:
2021+
version "5.0.0"
2022+
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz#249044356697b33b64f1a8f7c80922dddee7195c"
2023+
integrity sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==
2024+
dependencies:
2025+
cssesc "^2.0.0"
2026+
indexes-of "^1.0.1"
2027+
uniq "^1.0.1"
2028+
20342029
postcss@^7.0.6:
20352030
version "7.0.6"
20362031
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.6.tgz#6dcaa1e999cdd4a255dcd7d4d9547f4ca010cdc2"
@@ -2128,10 +2123,6 @@ readdirp@^2.0.0:
21282123
micromatch "^3.1.10"
21292124
readable-stream "^2.0.2"
21302125

2131-
regenerate@^1.2.1:
2132-
version "1.4.0"
2133-
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
2134-
21352126
regex-not@^1.0.0, regex-not@^1.0.2:
21362127
version "1.0.2"
21372128
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@@ -2143,24 +2134,6 @@ regexpp@^2.0.1:
21432134
version "2.0.1"
21442135
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
21452136

2146-
regexpu-core@^1.0.0:
2147-
version "1.0.0"
2148-
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
2149-
dependencies:
2150-
regenerate "^1.2.1"
2151-
regjsgen "^0.2.0"
2152-
regjsparser "^0.1.4"
2153-
2154-
regjsgen@^0.2.0:
2155-
version "0.2.0"
2156-
resolved "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
2157-
2158-
regjsparser@^0.1.4:
2159-
version "0.1.5"
2160-
resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c"
2161-
dependencies:
2162-
jsesc "~0.5.0"
2163-
21642137
release-zalgo@^1.0.0:
21652138
version "1.0.0"
21662139
resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730"
@@ -2681,6 +2654,11 @@ union-value@^1.0.0:
26812654
is-extendable "^0.1.1"
26822655
set-value "^0.4.3"
26832656

2657+
uniq@^1.0.1:
2658+
version "1.0.1"
2659+
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
2660+
integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
2661+
26842662
unset-value@^1.0.0:
26852663
version "1.0.0"
26862664
resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"

0 commit comments

Comments
 (0)