web-dev-qa-db-ja.com

Nodeを使用したSASSコンパイル中の変数の注入

私が作業しているアプリケーションでは、クライアントでレンダリングする前にSASSを動的にコンパイルする必要があります(キャッシュシステムが来るので、心配しないでください)。現在、私は node-sass を使用しており、すべてがうまく機能しています。

ここに私がこれまで取り組んでいるものがあります。簡潔にするため、他のプロジェクト固有のコードは削除されました。

_var sass            = require('node-sass'),
    autoprefixer    = require('autoprefixer-core'),
    vars            = require('postcss-simple-vars'),
    postcss         = require('postcss'),

function compileCSS() {
    var result = sass.renderSync({
            file: 'path/to/style.scss'
        });

    return postcss([autoprefixer]).process(result.css.toString()).css;
}
_

しわは、Nodeからの動的データを渡して、通常のSASS変数のようにコンパイルする必要があるということです。最初に PostCSS を使用してみました。変数インジェクションは それができること でした。残念なことに、それは機能しませんでした。

次に、 アンダースコア テンプレートを使用して、node-sass ' importer() を使用して上書きしようとしました:

_var result = sass.renderSync({
        file: 'path/to/style.scss',
        importer: function(url, prev, done) {
            var content = fs.readFileSync(partial_path),
                partial = _.template(content.toString());

            return {
                contents: partial({ test: 'test' })
            };
        }
    });
_

その結果、次のエラーが発生しました。

_Error: error reading values after :
_

明らかに、SASSはアンダースコアの変数構文を好まなかった。


TL; DR

Nodeアプリケーションから動的変数をSASSに渡すにはどうすればよいですか?


追加情報

  1. 私のチームと私は スタイラス ;のようなものに切り替えることに完全に不利ではありません。しかし、私たちはこれまでに大きな進歩を遂げており、それは苦痛になるでしょう。
25
Chris Wright

私は非常によく似た状況にいることに気づきました。 (変数として)全体で使用される動的な値/変数を受け入れる必要のある既存のSASSがたくさんありました。私はもともと一時ディレクトリ/ファイルを作成し、proxy_entry.scssvariables.scssとbootstrap実際のentry.scss目的のSASS変数が宣言されているこれは正常に機能し、目的の結果を達成しましたが、少し複雑に感じました...

非常に簡単な解決策があることが判明しましたnode-sassのoptions.data オプションのおかげで利用可能です。これは、「評価されるSASS文字列」を受け入れます。

型:文字列デフォルト:null特殊:ファイルまたはデータを指定する必要があります

レンダリングするためにlibsassに渡す文字列。 @importディレクティブを使用するときにlibsassがファイルを検索できるように、これと組み合わせてincludePathsを使用することをお勧めします。

これにより、すべての一時ディレクトリとファイルを作成/管理する必要が完全になくなりました。

ビジュアルTL; DR

Dynamic Variables in SASS with node-sass

解決策はこのようなものになります

1.)通常どおりsassOptionsを定義します

var sassOptionsDefaults = {
  includePaths: [
    'some/include/path'
  ],
  outputStyle:  'compressed'
};

2.)options.dataの「動的SASS文字列」を書く

var dataString =
  sassGenerator.sassVariables(variables) +
  sassGenerator.sassImport(scssEntry);
var sassOptions = _.assign({}, sassOptionsDefaults, {
  data: dataString
});

3.)通常どおりSASSを評価する

var sass = require('node-sass');
sass.render(sassOptions, function (err, result) {
  return (err)
    ? handleError(err);
    : handleSuccess(result.css.toString());
});

注意:これは、entry.scssが変数を「デフォルト」として定義するvariables.scssをインポートすることを前提としています。

// variables.scss
$someColor: blue !default;
$someFontSize: 13px !default;

// entry.scss
@import 'variables';
.some-selector { 
  color: $someColor;
  font-size: $someFontSize;
}

例としてすべてをつなぎ合わせる

var sass = require('node-sass');

// 1.) Define sassOptions as usual
var sassOptionsDefaults = {
  includePaths: [
    'some/include/path'
  ],
  outputStyle:  'compressed'
};

function dynamicSass(scssEntry, variables, handleSuccess, handleError) {
  // 2.) Dynamically create "SASS variable declarations"
  // then import the "actual entry.scss file".
  // dataString is just "SASS" to be evaluated before
  // the actual entry.scss is imported.
  var dataString =
    sassGenerator.sassVariables(variables) +
    sassGenerator.sassImport(scssEntry);
  var sassOptions = _.assign({}, sassOptionsDefaults, {
    data: dataString
  });

  // 3.) render sass as usual
  sass.render(sassOptions, function (err, result) {
    return (err)
      ? handleError(err);
      : handleSuccess(result.css.toString());
  });
}

// Example usage.
dynamicSass('some/path/entry.scss', {
  'someColor': 'red',
  'someFontSize': '18px'
}, someSuccessFn, someErrorFn);

「sassGenerator」関数は次のようになります

function sassVariable(name, value) {
  return "$" + name + ": " + value + ";";
}

function sassVariables(variablesObj) {
  return Object.keys(variablesObj).map(function (name) {
    return sassVariable(name, variablesObj[name]);
  }).join('\n')
}

function sassImport(path) {
  return "@import '" + path + "';";
}

これにより、以前と同じようにSASSを書くことができます必要な場所でSASS変数を使用する。また、 "特別な動的sass実装"に拘束されることもありません(つまり、.scssファイル全体で "アンダースコア/ lodashテンプレートを使用することを回避します)。また、IDE機能、リントなど...あなたは今ちょうど通常のSASSの記述に戻るなので、まったく同じです。

さらに、Gulpなどを介して複数の値セットを指定してentry.scssの複数のバリエーションをプリコンパイルするなど、node/http/on-comp-on-the-fly以外の使用法にもうまく変換されます。

これが@ChrisWright(およびその他)の役に立てば幸いです!私は主題に関する情報を見つけるのに苦労したことを知っており、これはかなり一般的なユースケースだと思います(動的な値をデータベース、構成、HTTPパラメータなどからSASSに渡すことを望んでいます...)。

59
Erik Aybar

Node-sass ' importer() メソッドに頭を包んだ後、これを解決できました。私のソリューションは、アンダースコアテンプレートを使用し、入ってくるファイルを手動で読み取ることでした。これは最もエレガントで効率的なソリューションではありませんが、生成されたページごとに1回だけ実行されます。その後、ファイルは縮小され、将来のリクエストのためにキャッシュされます。

// Other none-sass render parameters omitted for brevity
importer: function (url, prev, done) {

    // Manually read each partial file as it's encountered
    fs.readFile(url, function (err, result) {
        if (err) {

            // If there is an error reading the file, call done() with
            // no content to avoid a crash
            return done({
                contents: ''
            });
        }

        // Create an underscore template out of the partial
        var partial = _.template(result.toString());

        // Compile template and return its contents to node-sass for
        // compilation with the rest of the SCSS partials
        done({
            contents: partial({ data: YOUR_DATA_OBJECT })
        });
    });
}

このソリューションを使用すると、SCSSパーシャル内の通常のアンダースコア変数構文を参照できます。例として:

body {
    color: <%= data.colour %>;
}
2
Chris Wright

NodeではなくJavaで。同様の問題を解決しました。Webサイトにアクセスしているクライアントに基づいて、Webサイトのテーマを生成するデータベースからSASS変数をレンダリングする必要がありました。

私はいくつかの解決策を探り、このサードパーティのサービスに出会いました https://www.grooveui.com 。この問題を解決するための言語に依存しないソリューションを提供します。

0
Anubhav