<===> options.yml
---
:todo:
- dart-sass

<===> input.scss
// ----
// Sass (v3.4.21)
// Compass (v1.0.3)
// ----

@function remove-modifiers($selector) {
    // convert selector to a string
    $selector: inspect(nth($selector, 1));
  
    $modifier: '';
  
    // Find out where the first modifier starts
    $modifier-index: str-index($selector, '"--');
  
    @if $modifier-index {
      // Strip the first part of the selector up until the first modifier
      $modifier: str-slice($selector, $modifier-index);
      // Find out where the modifier ends
      $modifier-end: str-index($modifier, '"]');
      // Isolate the modifier
      $modifier: str-slice($modifier, 0, $modifier-end);
      // Remove the modifier from the selector
      $selector: str-replace($selector, $modifier, '');
      // Remove junk characters
      $selector: str-replace($selector, '[class*=]', '');
      // Recurse the function to eliminate any remainig modifiers
      $selector: remove-modifiers($selector);
    }
  
    @return $selector;
  }
  
  @function remove-duplicates($list, $recursive: false) {
    $result: ();
    
    @each $item in $list {
      @if not index($result, $item) {
        @if length($item) > 1 and $recursive {
          $result: append($result, remove-duplicates($item, $recursive));
        }
        @else {
          $result: append($result, $item);
        }
      }
    }
    
    @return $result;
  }
  
  @function str-replace($string, $search, $replace) { 
    $index: str-index($string, $search);
  
    @if $index {
      @return str-slice($string, 1, $index - 1) + $replace + str-replace(
        str-slice($string, $index + str-length($search)), $search, $replace
      );
    }
  
    @return $string;
  }
  
  @function module-tree($selector) {
    $parent-module: $module;
  
    // Remove any modifers
    $selectors: remove-modifiers($selector);
  
    // Remove any junk characters
    $selectors: str-replace($selectors, '.', '');
    $selectors: str-replace($selectors, '[class*="--', '');
    $selectors: str-replace($selectors, '[class*="', '');
    $selectors: str-replace($selectors, '--"]', '');
    $selectors: str-replace($selectors, '"]', '');
  
    // Spoof our modules into a list
    $selectors: str-replace($selectors, ' ', ', ');
    $selectors: selector-parse($selectors);
  
    @return $selectors;
  }
  
  @function list-remove($list, $value, $recursive: false) { 
      $result: ();
  
      @for $i from 1 through length($list) {
          @if type-of(nth($list, $i)) == list and $recursive {
              $result: append($result, list-remove(nth($list, $i), $value, $recursive), comma);
          } @else if nth($list, $i) != $value {
              $result: append($result, nth($list, $i), comma);
          }
      }
  
      @return $result;
   }
   
  @function this($options...) {
    $value: map-get($config, $options...);
    $debug: true;
  
    $this: &;
  
    @if length($this) > 0 {
      @if str-index(inspect(nth($this, 1)), '%') == 1 {
        $debug: false;
      }
    }
  
    @if $debug and not $value and $value != false {
        @warn '#{$options} not found in #{$module} config';
    }
  
    @return $value;
  }
  
  @function config($map-old, $map-new) {
      // Merge default and custom options
      $map-merged: map-merge($map-old, $map-new);
    
      // Store config in global variable
      $config: $map-merged !global;
  
      // Return merged map
      @return $map-merged;
  }
  
  @mixin module($module: $module) {
    $nested: &;
  
    @if not $nested {
      $module: $module !global;
    }
    
    $selectors: ();
  
    @each $item in $module {
      $selectors: join($selectors, '.#{$module}', comma);
      $selectors: join($selectors, '[class*="#{$module}--"]', comma);
    }
      
    #{$selectors} {
      @content;
    }
  }
  
  @mixin component($components: null, $glue: '__') {
      $selectors: '[class*="#{$module}#{$glue}"]';
      $this: &;
      $tree: module-tree($this);
      $namespace: nth($tree, length($tree));
  
      @if $components {
        $selectors: ();
  
        @each $component in $components {
          $selectors: join(
            $selectors, 
            '.#{$namespace}#{$glue}#{$component}, [class*="#{$namespace}#{$glue}#{$component}-"]', 
            comma
          );
        }
      }
  
      $parents: ();
  
      @each $selector in & {
        // spoof the selector into a list
        $selector: str-replace(inspect($selector), ' ', ', ');
        $selector: selector-parse($selector);
  
        // if the last item isn't a modifier, remove it
        @if not str-index(inspect(nth($selector, length($selector))), '[class*="--') {
          $selector: list-remove($selector, nth($selector, length($selector)));
        }
  
        // convert the selector back into a string
        @if length($selector) == 1 {
          $selector: nth($selector, 1);
        }
        $selector: str-replace(inspect($selector), ', ', ' ');
  
        // Re-build the parent selector
        $parents: append($parents, $selector, comma);
      }
  
      // remove duplicate selectors
      $parents: remove-duplicates($parents);
  
      @if length($parents) == 1 {
        $parents: nth($parents, 1);
      }
  
      @if ($parents == '()') {
        @at-root #{$selectors} {
          @content;
        }
      } @else {
        @at-root #{$parents} {
          #{$selectors} {
              @content;
          }
        }
      }
  
  }
  
  @mixin modifier($modifier) {
    $selectors: &;
  
    @if str-index(inspect(&), '.#{$module}') {
      $selectors: ();
  
      @for $i from 1 through length(&) {
        @if $i % 2 == 0 {
          $selectors: append($selectors, nth(&, $i), comma);
        }
      }
    }
  
    @at-root #{$selectors} {
      $modifier-selectors: ();
      
      @each $item in $modifier {
        $modifier-selectors: join(
          $modifier-selectors, '&[class*="--#{$modifier}"]', comma
        );
      }
  
      #{$modifier-selectors} {
        @content;
      }
    }
  }
  
  @mixin button($custom:()) {
    $buttons: config((
      'group-spacing': 1em
    ), $custom);
  
    @include module('button') {
      @include component('group') {
        content: 'fizzbuzz';
        @include module {
          margin-left: this('group-spacing');
          &:first-child {
            margin-left: 0;
          }
        }
      }
    }
  }
  
  @include button();
  
  @include module('modal') {
    @include modifier('animate') {
      @include modifier('zoom') {
        content: "fizzbuzz"
      }
    }
  }
<===> output.css
.button__group, [class*="button__group-"] {
  content: 'fizzbuzz';
}
.button__group .button, .button__group [class*="button--"], [class*="button__group-"] .button, [class*="button__group-"] [class*="button--"] {
  margin-left: 1em;
}
.button__group .button:first-child, .button__group [class*="button--"]:first-child, [class*="button__group-"] .button:first-child, [class*="button__group-"] [class*="button--"]:first-child {
  margin-left: 0;
}

[class*="modal--"][class*="--animate"][class*="--zoom"] {
  content: "fizzbuzz";
}

<===> warning-libsass
DEPRECATION WARNING on line 120 of /sass/spec/libsass-issues/issue_2520/input.scss:
!global assignments won't be able to declare new variables in future versions.
Consider adding `$config: null` at the top level.

DEPRECATION WARNING on line 130 of /sass/spec/libsass-issues/issue_2520/input.scss:
!global assignments won't be able to declare new variables in future versions.
Consider adding `$module: null` at the top level.