All files / packages/sds-subsystems/scripts/plugins postcss-add-scope.js

100% Statements 23/23
95.23% Branches 20/21
100% Functions 6/6
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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    1x     1x 16x       14x       1x 14x       15x 1x             14x 14x         13x     2x   12x 16x 2x     14x 1x 1x       13x     12x           1x 1x              
const conditionalGroupRules = ['media', 'supports', 'document', 'html'];
 
// check if the node is already scoped
const alreadyScoped = (selector, scope) => {
  let outermostScope = selector.split(' ', 1)[0];
  return outermostScope === scope;
};
 
const isValidScope = (scope) => {
  return scope && scope.indexOf(',') === -1;
};
 
// check if a rule should be scoped or skipped.
const isRuleScopable = (rule) => {
  return rule.parent.type !== 'root'
    ? rule.parent.type === 'atrule' && conditionalGroupRules.indexOf(rule.parent.name) > -1
    : true;
};
 
// root level pseudo-class selectors must not be scoped.
const isRootLevelPseudoClass = (selector) => /^:(\w|-)+$/.test(selector.trim());
 
const plugin = (opts = {}) => {
  return {
    postcssPlugin: 'postcss-add-scope',
    Once(root, { result }) {
      // default scope set to .slds-scope
      let scope = opts.scope || '.slds-scope';
      if (!isValidScope(scope)) {
        throw root.error('invalid scope', { plugin: 'postcss-add-scope' });
      }
 
      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 (alreadyScoped(selector, scope) || isRootLevelPseudoClass(selector)) {
            return selector;
          }
 
          const dirRegex = /^\[dir=["']\w+["']\]/;
          if (dirRegex.test(selector)) {
            const [dirPart, restOfSelector] = selector.match(dirRegex)
              ? [selector.match(dirRegex)[0], selector.replace(dirRegex, '').trim()]
              : [null, selector];
            return `${dirPart} ${scope} ${restOfSelector}`;
          }
 
          // special case for a top level '&' selector, resolves to scope
          if (selector.includes('&')) {
            return selector.replace(/&/g, scope);
          }
 
          return `${scope} ${selector}`;
        });
      });
    },
  };
};
 
plugin.postcss = true;
 
export default plugin;