разделить полученный код css из источника scss на несколько файлов css на основе используемого миксина sass

Мне нужно написать шаблон html5 с разными «ароматами» или цветовыми вариантами.

Я хотел бы иметь один файл scss для работы, но несколько файлов css для рендеринга.

Допустим, моя точка входа в scss — это app.scss.

ИМХО, идеальным подходом было бы что-то вроде:

$flavors: (
    flavor-a: (
        background: white
    ),
    flavor-b: (
        background: grey
    )
);

@mixin flavor($name) {
    /* parser-rule-start */
    @content;
    /* parser-rule-end */
}

html {
    /* regular rule - valid for all flavors => goes to app.css */
    font-family: sans-serif;
    @each $name, $options in $flavors {
        @include flavor($name) {
            /* flavor-rule => goes to flavor-a.css / flavor-b.css */
            background: map-get($options, 'background');
        }
    }
}

так что я в конечном итоге с

  • app.css
  • аромат-a.css
  • вкус-b.css

У меня было это требование раньше, и я решил его с помощью нескольких файлов ввода, которые вызывают миксины для раскрашивания и так далее.

Но мне не нравится такой подход, потому что после того, как я закодирую scss для нового компонента, мне нужно будет переместить фрагменты строк из файла структуры в примесь-приправа, которая вызывается в файлах ввода приправы-*.scss.

Банкомат моей сборки выглядит так (глотком):

/**
 * compile/concat scss
 */
gulp.task('css', function () {

    const sassOptions = {
        outputStyle: "compressed",
        errorLogToConsole: true
    };

    const autoprefixerOptions = {
        browsersList: [
            "last 2 versions",
            "ie >= 11"
        ]
    };

    return gulp
        .src("src/scss/*.scss")
        .pipe(sourcemaps.init())
        .pipe(sass(sassOptions).on('error', makeErrorLogger('css')))
        .pipe(autoprefixer(autoprefixerOptions))
//      .pipe(splitFlavors()) <- the point i would need some magic
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest("public/static/css"))
        .pipe(browserSync.stream({match: '**/*.css'}));
});

Кто-нибудь знает плагин gulp для этой цели или мне нужно самому его кодировать?


person Philipp Wrann    schedule 17.10.2019    source источник
comment
Просто пытаюсь немного лучше понять вопрос - можете ли вы объяснить проблему с решением для нескольких входных файлов? Я не понимаю, почему это не решит вашу проблему.   -  person wiiiiilllllll    schedule 17.10.2019
comment
Можно ли вместо этого иметь две точки входа? Если бы flavor-a.scss и flavor-b.scss должны были импортировать общий частичный _app.scss, вы могли бы разделить вывод с помощью миксина. См. мой ответ на этот похожий вопрос: stackoverflow.com/questions/58284016/   -  person Cody MacPixelface    schedule 17.10.2019
comment
@wiiiiillllllll Несколько точек входа в основном решают проблему, поэтому речь идет о том, как упростить ее обслуживание. При использовании нескольких файлов точек входа мне нужно извлечь все правила, специфичные для вкуса (поэтому я получаю 2 включения для каждого компонента) в отдельный include/mixin. Использование разделения, как описано выше, сделает это проще. Я мог бы просто определить правила, которые отличаются от вкуса к вкусу, во включаемом блоке и прокручивать какой-то массив вкусов...   -  person Philipp Wrann    schedule 22.10.2019
comment
@Trollsyn Да, это вариант, который я использовал раньше, но я прошу способ, которым, по моему мнению, проще писать/обслуживать.   -  person Philipp Wrann    schedule 22.10.2019
comment
Вы смотрели на ответ @Trollsyn? Похоже, он будет делать то, что вы хотите (и выглядит действительно круто, я буду использовать это в будущем — слава Тролльсину!)   -  person wiiiiilllllll    schedule 22.10.2019


Ответы (1)


ОБНОВЛЕНИЕ 3

еще одно усыновление на замену

let newSelector = selector.replace(/\s?\:\:flavor\-([^\s]+)/g, "").trim();

ОБНОВЛЕНИЕ 2

замена в селекторе правила css должна быть принята

let newSelector = selector.replace(/\s?\:\:flavor\-([a-zA-Z0-9\-\_\s]+)/g, "").trim();

ОБНОВЛЕНИЕ

используя модифицированный селектор, а не свойства, вы можете продолжать использовать вложенные правила внутри блока «for-flavor».

Итак, настройте миксин, чтобы он выглядел так:

@mixin for-flavor($name) {
  ::flavor-#{$name} & {
    @content;
  }
}

и задача глотка:

const path = require("path");
const gulp = require("gulp");
const sourcemaps = require("gulp-sourcemaps");
const sass = require("gulp-sass");
const autoprefixer = require("gulp-autoprefixer");
const through = require("through2");
const postcss = require("gulp-postcss");

/**
 * compile/concat scss
 */
gulp.task('css', function () {

    const sassOptions = {
        outputStyle: "compressed",
        errorLogToConsole: true
    };

    const autoprefixerOptions = {
        browsersList: [
            "last 2 versions",
            "ie >= 11"
        ]
    };

    function addFlavorFiles() {
        return through.obj(function(file, encoding, callback) {
            /* @var file File */
            let content = file.contents.toString();
            let names = [];
            let matches = content.match(/\:\:flavor\-([^\s\{]+)/g);
            if (matches) {
                names = matches.map(match => match.replace(/\:\:flavor\-/, '').trim());
                // unique
                names = names.filter((el, index, arr) => {
                    return index === arr.indexOf(el);
                });
            }
            names.forEach(name => {
                let newFile = file.clone();
                newFile.contents = Buffer.concat([Buffer.from(`/*!flavor:${name}*/\n`, encoding), file.contents]);
                let filePath = path.parse(file.path);
                newFile.path = path.join(filePath.dir, `flavor-${name + filePath.ext}`);
                this.push(newFile);
            });
            callback(null, file);
        })
    }

    function filterFlavors(css, opts) {
        let flavor = null;
        if (css.nodes[0].type === "comment" && css.nodes[0].text.indexOf('!flavor:') === 0) {
            flavor = css.nodes[0].text.replace(/^\!flavor\:/, "").trim();
        }
        css.walkRules(rule => {
            let selector = rule.selector;
            if (/^\:\:flavor\-/.test(selector)) {
                // flavor rule
                if (flavor === null) {
                    // general file, all flavor rules must go...
                    rule.remove();
                } else {
                    let matches = selector.match(/\:\:flavor\-([a-zA-Z0-9\-\_]+)/);
                    let currentFlavor = matches[1];
                    if (flavor !== currentFlavor) {
                        // wrong flavor
                        rule.remove();
                    } else {
                        // keep rule but adjust selector
                        let newSelector = selector.replace(/^\:\:flavor\-([a-zA-Z0-9\-\_]+)/, "").trim();
                        rule.selector = newSelector;
                    }
                }
            } else if(flavor !== null) {
                // general rule but flavor file, so remove the rule
                rule.remove();
            }
        });
        css.walkRules(rule => {
            if (!rule.nodes || rule.nodes.length === 0) {
                rule.remove();
            }
        });
        css.walkAtRules(atRule => {
            if (!atRule.nodes  || atRule.nodes.length === 0) {
                atRule.remove();
            }
        });
        // optional: delete all font-face definitions from flavor file
        if (flavor !== null) {
            css.walkAtRules(atRule => {
                if (atRule.name === "font-face") {
                    atRule.remove();
                }
            });
        }
    }

    return gulp
        .src("src/scss/*.scss")
        .pipe(sourcemaps.init())
        .pipe(sass(sassOptions).on('error', makeErrorLogger('css')))
        .pipe(addFlavorFiles())
        .pipe(autoprefixer(autoprefixerOptions))
        .pipe(postcss([filterFlavors]))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest("public/static/css"))
});

ИСХОДНОЕ СООБЩЕНИЕ

Мне удалось решить это, как я собирался

Используйте миксин sass, который добавляет правила для синтаксического анализа:

@mixin for-flavor($name) {
  -flavor-start: unquote($name);
  @content;
  -flavor-end: unquote($name);
}

используйте его для своих объявлений css

// you get the idea...
$flavors: (
  "lightblue": (
    background: lightblue,
  ),
  "pink": (
    background: pink,
  ),
  "dark": (
    background: black
  ),
);

#page-header {
  background: black;
  @each $name, $options in $flavors {
    @if map_has_key($options, 'background') {
      @include for-flavor($name) {
        background: map_get($options, 'background');
      }
    }
  }
  @include for-flavor("lightblue") {
    /* special rule for specific flavor */
    color: black;
  }
}

глоток

const path = require("path");
const gulp = require("gulp");
const sourcemaps = require("gulp-sourcemaps");
const sass = require("gulp-sass");
const autoprefixer = require("gulp-autoprefixer");
const through = require("through2");
const postcss = require("gulp-postcss");

/**
 * compile/concat scss
 */
gulp.task('css', function () {

    const sassOptions = {
        outputStyle: "compressed",
        errorLogToConsole: true
    };

    const autoprefixerOptions = {
        browsersList: [
            "last 2 versions",
            "ie >= 11"
        ]
    };

    function addFlavorFiles() {
        return through.obj(function(file, encoding, callback) {
            /* @var file File */
            let content = file.contents.toString();
            let names = [];
            let matches = content.match(/\-flavor\-start\:([^\;]+)/g);
            if (matches) {
                names = matches.map(match => match.replace(/\-flavor\-start\:/, '').trim());
            }
            names.forEach(name => {
                let newFile = file.clone();
                newFile.contents = Buffer.concat([Buffer.from(`/*!flavor:${name}*/\n`, encoding), file.contents]);
                let filePath = path.parse(file.path);
                newFile.path = path.join(filePath.dir, `flavor-${name + filePath.ext}`);
                this.push(newFile);
            });
            callback(null, file);
        })
    }

    function filterFlavors(css, opts) {
        let flavor = null;
        if (css.nodes[0].type === "comment" && css.nodes[0].text.indexOf('!flavor:') === 0) {
            flavor = css.nodes[0].text.replace(/^\!flavor\:/, "").trim();
        }
        let inFlavorBlock = "";
        css.walkDecls(decl => {
            let prop = decl.prop;
            let isControlProp = false;
            let value = decl.value.trim();
            if (prop === "-flavor-end") {
                inFlavorBlock = "";
                isControlProp = true;
            } else if (prop === "-flavor-start") {
                inFlavorBlock = value;
                isControlProp = true;
            }
            let isValid = ((inFlavorBlock === "" && flavor === null) || inFlavorBlock === flavor);
            if (isValid === false || isControlProp) {
                decl.remove();
            }
        });
        css.walkRules(rule => {
            if (!rule.nodes || rule.nodes.length === 0) {
                rule.remove();
            }
        });
        css.walkAtRules(atRule => {
            if (!atRule.nodes  || atRule.nodes.length === 0) {
                atRule.remove();
            }
        });
    }

    return gulp
        .src("src/scss/*.scss")
        .pipe(sourcemaps.init())
        .pipe(sass(sassOptions))
        .pipe(addFlavorFiles())
        .pipe(autoprefixer(autoprefixerOptions))
        .pipe(postcss([filterFlavors]))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest("public/static/css"))
});

Единственная оставшаяся проблема заключается в том, что исходные карты не фильтруются...

person Philipp Wrann    schedule 24.10.2019