Skip to content

Commit 066f97e

Browse files
committed
Extend now works with selector lists. Closes #17.
1 parent d1e277c commit 066f97e

12 files changed

+353
-221
lines changed

dist/j2c.amd.js

+56-42
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,37 @@ define(function () { 'use strict';
3737
) || a + b
3838
}
3939

40+
// "Tokenizes" the selectors into parts relevant for the next function.
41+
// Strings and comments are matched, but ignored afterwards.
42+
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g
43+
44+
/**
45+
* This will split a coma-separated selector list into individual selectors,
46+
* ignoring comas in strings and in :pseudo-selectors(parameter, lists).
47+
* regexp generated by scripts/regexps.js then trimmed by hand
48+
* @param {string} selector
49+
* @return {string[]}
50+
*/
51+
52+
function splitSelector(selector) {
53+
var indices = [], res = [], inParen = 0, match, i
54+
/*eslint-disable no-cond-assign*/
55+
while(match = selectorTokenizer.exec(selector)) {
56+
/*eslint-enable no-cond-assign*/
57+
switch(match[0]){
58+
case '(': inParen++; break
59+
case ')': inParen--; break
60+
case ',': if (inParen) break; indices.push(match.index)
61+
}
62+
}
63+
for (i = indices.length; i--;){
64+
res.unshift(selector.slice(indices[i] + 1))
65+
selector = selector.slice(0, indices[i])
66+
}
67+
res.unshift(selector)
68+
return res
69+
}
70+
4071
function decamelize(match) {
4172
return '-' + match.toLowerCase()
4273
}
@@ -136,7 +167,7 @@ define(function () { 'use strict';
136167
*/
137168

138169
function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){
139-
var kk
170+
var kk, i
140171
if (/^@(?:namespace|import|charset)$/.test(k)) {
141172
if(type.call(v) == ARRAY){
142173
for (kk = 0; kk < v.length; kk++) {
@@ -162,27 +193,32 @@ define(function () { 'use strict';
162193
buf.c('}\n')
163194

164195
} else if (/^@extends?$/.test(k)) {
165-
166-
/*eslint-disable no-cond-assign*/
167-
// pick the last class to be extended
168-
while (kk = findClass.exec(rawPrefix)) k = kk[4]
169-
/*eslint-enable no-cond-assign*/
170-
if (k == null || !local) {
171-
// we're in a @global{} block
172-
buf.a('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n')
173-
return
174-
} else if (/^@extends?$/.test(k)) {
175-
// no class in the selector
176-
buf.a('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n')
196+
if (!local) {
197+
buf.c('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n')
177198
return
178199
}
179-
ns.e(
180-
type.call(v) == ARRAY ? v.map(function (parent) {
181-
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l)
182-
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l),
183-
k
184-
)
185-
200+
rawPrefix = splitSelector(rawPrefix)
201+
for(i = 0; i < rawPrefix.length; i++) {
202+
/*eslint-disable no-cond-assign*/
203+
// pick the last class to be extended
204+
while (kk = findClass.exec(rawPrefix[i])) k = kk[4]
205+
/*eslint-enable no-cond-assign*/
206+
if (k == null) {
207+
// the last class is a :global(.one)
208+
buf.c('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix[i]), ';\n')
209+
continue
210+
} else if (/^@extends?$/.test(k)) {
211+
// no class in the selector, therefore `k` hasn't been overwritten.
212+
buf.c('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix[i]), ';\n')
213+
continue
214+
}
215+
ns.e(
216+
type.call(v) == ARRAY ? v.map(function (parent) {
217+
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l)
218+
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l),
219+
k
220+
)
221+
}
186222
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) {
187223
if (type.call(v) === ARRAY) {
188224
for (kk = 0; kk < v.length; kk++) {
@@ -212,28 +248,6 @@ define(function () { 'use strict';
212248
}
213249
}
214250

215-
// This will split a coma-separated selector list into individual selectors,
216-
// ignoring comas in strings and in :pseudo-selectors(parameter, lists).
217-
// regexp generated by scripts/regexps.js then trimmed by hand
218-
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g
219-
220-
function splitSelector(selector) {
221-
var indices = [], res = [], inParen = 0, match, i
222-
while(match = selectorTokenizer.exec(selector)) {
223-
switch(match[0]){
224-
case '(': inParen++; break
225-
case ')': inParen--; break
226-
case ',': if (inParen) break; indices.push(match.index)
227-
}
228-
}
229-
for (i = indices.length; i--;){
230-
res.unshift(selector.slice(indices[i] + 1))
231-
selector = selector.slice(0, indices[i])
232-
}
233-
res.unshift(selector)
234-
return res
235-
}
236-
237251
/**
238252
* Add rulesets and other CSS statements to the sheet.
239253
*

dist/j2c.amd.min.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/j2c.commonjs.js

+56-42
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,37 @@ function concat(a, b, selectorP) {
3737
) || a + b
3838
}
3939

40+
// "Tokenizes" the selectors into parts relevant for the next function.
41+
// Strings and comments are matched, but ignored afterwards.
42+
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g
43+
44+
/**
45+
* This will split a coma-separated selector list into individual selectors,
46+
* ignoring comas in strings and in :pseudo-selectors(parameter, lists).
47+
* regexp generated by scripts/regexps.js then trimmed by hand
48+
* @param {string} selector
49+
* @return {string[]}
50+
*/
51+
52+
function splitSelector(selector) {
53+
var indices = [], res = [], inParen = 0, match, i
54+
/*eslint-disable no-cond-assign*/
55+
while(match = selectorTokenizer.exec(selector)) {
56+
/*eslint-enable no-cond-assign*/
57+
switch(match[0]){
58+
case '(': inParen++; break
59+
case ')': inParen--; break
60+
case ',': if (inParen) break; indices.push(match.index)
61+
}
62+
}
63+
for (i = indices.length; i--;){
64+
res.unshift(selector.slice(indices[i] + 1))
65+
selector = selector.slice(0, indices[i])
66+
}
67+
res.unshift(selector)
68+
return res
69+
}
70+
4071
function decamelize(match) {
4172
return '-' + match.toLowerCase()
4273
}
@@ -136,7 +167,7 @@ var findClass = /()(?::global\(\s*(\.[-\w]+)\s*\)|(\.)([-\w]+))/g
136167
*/
137168

138169
function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){
139-
var kk
170+
var kk, i
140171
if (/^@(?:namespace|import|charset)$/.test(k)) {
141172
if(type.call(v) == ARRAY){
142173
for (kk = 0; kk < v.length; kk++) {
@@ -162,27 +193,32 @@ function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){
162193
buf.c('}\n')
163194

164195
} else if (/^@extends?$/.test(k)) {
165-
166-
/*eslint-disable no-cond-assign*/
167-
// pick the last class to be extended
168-
while (kk = findClass.exec(rawPrefix)) k = kk[4]
169-
/*eslint-enable no-cond-assign*/
170-
if (k == null || !local) {
171-
// we're in a @global{} block
172-
buf.a('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n')
173-
return
174-
} else if (/^@extends?$/.test(k)) {
175-
// no class in the selector
176-
buf.a('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix), ';\n')
196+
if (!local) {
197+
buf.c('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix), ';\n')
177198
return
178199
}
179-
ns.e(
180-
type.call(v) == ARRAY ? v.map(function (parent) {
181-
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l)
182-
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l),
183-
k
184-
)
185-
200+
rawPrefix = splitSelector(rawPrefix)
201+
for(i = 0; i < rawPrefix.length; i++) {
202+
/*eslint-disable no-cond-assign*/
203+
// pick the last class to be extended
204+
while (kk = findClass.exec(rawPrefix[i])) k = kk[4]
205+
/*eslint-enable no-cond-assign*/
206+
if (k == null) {
207+
// the last class is a :global(.one)
208+
buf.c('@-error-cannot-extend-in-global-context ', JSON.stringify(rawPrefix[i]), ';\n')
209+
continue
210+
} else if (/^@extends?$/.test(k)) {
211+
// no class in the selector, therefore `k` hasn't been overwritten.
212+
buf.c('@-error-no-class-to-extend-in ', JSON.stringify(rawPrefix[i]), ';\n')
213+
continue
214+
}
215+
ns.e(
216+
type.call(v) == ARRAY ? v.map(function (parent) {
217+
return parent.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l)
218+
}).join(' ') : v.replace(/()(?::global\(\s*(\.[-\w]+)\s*\)|()\.([-\w]+))/, ns.l),
219+
k
220+
)
221+
}
186222
} else if (/^@(?:font-face$|viewport$|page )/.test(k)) {
187223
if (type.call(v) === ARRAY) {
188224
for (kk = 0; kk < v.length; kk++) {
@@ -212,28 +248,6 @@ function at(k, v, buf, prefix, rawPrefix, vendors, local, ns){
212248
}
213249
}
214250

215-
// This will split a coma-separated selector list into individual selectors,
216-
// ignoring comas in strings and in :pseudo-selectors(parameter, lists).
217-
// regexp generated by scripts/regexps.js then trimmed by hand
218-
var selectorTokenizer = /[(),]|"(?:\\.|[^"\n])*"|'(?:\\.|[^'\n])*'|\/\*[\s\S]*?\*\//g
219-
220-
function splitSelector(selector) {
221-
var indices = [], res = [], inParen = 0, match, i
222-
while(match = selectorTokenizer.exec(selector)) {
223-
switch(match[0]){
224-
case '(': inParen++; break
225-
case ')': inParen--; break
226-
case ',': if (inParen) break; indices.push(match.index)
227-
}
228-
}
229-
for (i = indices.length; i--;){
230-
res.unshift(selector.slice(indices[i] + 1))
231-
selector = selector.slice(0, indices[i])
232-
}
233-
res.unshift(selector)
234-
return res
235-
}
236-
237251
/**
238252
* Add rulesets and other CSS statements to the sheet.
239253
*

0 commit comments

Comments
 (0)