-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathindex.js
95 lines (81 loc) · 2.33 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
'use strict';
const conditionalGroupRules = ['media','supports','document'];
const isObject = item => {
return item != null && typeof item === 'object' && Array.isArray(item) === false;
}
/**
* Determine if selector is already scoped
*
* @param {string} selector
* @param {string} scope
*/
const isScopeApplied = (selector, scope) => {
const selectorTopScope = selector.split(" ",1)[0];
return selectorTopScope === scope;
}
/**
* Determine if scope is valid
*
* @param {string} scope
*/
const isValidScope = scope => {
if (!scope){
return false;
}
return scope.indexOf(',') === -1;
}
/**
* Determine if rule should be scoped
*
* @param {Rule} rule
*/
const isRuleScopable = rule => {
if(rule.parent.type !== 'root') {
return rule.parent.type === 'atrule' && conditionalGroupRules.indexOf(rule.parent.name) > -1;
} else {
return true;
}
}
/**
* extract the scope from the input given by caller
*
* @param {string | Record<string, string>} options
*/
const extractScope = (options) => {
if (typeof options === 'string') {
return options;
} else if (isObject(options) && options.scope) {
return options.scope
}
return null;
}
const scopify = (options) => {
return {
postcssPlugin: 'postcss-scopify',
Once (root, { result }) {
const scope = extractScope(options);
// guard statment- allow only valid scopes
if(!isValidScope(scope)){
throw root.error('invalid scope', { plugin: 'postcss-scopify' });
}
root.walkRules(rule => {
// skip scoping of special rules (certain At-rules, nested, etc')
if(!isRuleScopable(rule)){
return rule;
}
rule.selectors = rule.selectors.map(selector => {
if (isScopeApplied(selector,scope)) {
return selector;
}
// special case for a top level '&' selector, resolves to scope
if (selector.includes('&')) {
return selector.replace(/&/g, scope);
}
return scope + ' ' + selector;
});
});
},
};
}
module.exports = scopify;
module.exports.postcss = true;