根据使用过的sass mixin将来自CSS源的结果CSS代码拆分为多个CSS文件

问题描述 投票:0回答:1

我需要编写一个具有不同“风味”或颜色变量的html5模板。

我想使用一个CSS文件,但要渲染多个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
  • flavor-a.css
  • flavor-b.css

我之前有这个要求,并用多个条目文件解决了这个问题,这些文件调用mixins进行着色等等。

但是我不喜欢这种方法,因为在为新组件编码了scss之后,我需要将几行行从结构文件移动到flavor-mixin,这在flavor-*。scss条目文件中称为。

我的构建的ATM看起来像(吞咽):

/**
 * 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插件,还是我必须自己编写它?

sass gulp gulp-sass
1个回答
0
投票

UPDATE 3

另一项替代采用

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

UPDATE 2

需要使用css规则的选择器中的替换项

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

UPDATE

通过使用修改后的选择器而不是属性,您可以继续在“ for-flavor”块内使用嵌套规则。

因此将mixin调整为:

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

和gulp任务:

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,添加分析规则:

@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;
  }
}

gulp

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"))
});

唯一剩下的问题是,未过滤源地图...

© www.soinside.com 2019 - 2024. All rights reserved.