web-dev-qa-db-fra.com

Injection de variables lors de la compilation SASS avec Node

Dans une application sur laquelle je travaille, je dois compiler SASS de manière dynamique avant de faire un rendu sur le client (le système de mise en cache arrive, ne vous inquiétez pas). Actuellement, j'utilise node-sass et tout fonctionne très bien.

Voici ce sur quoi je travaille jusqu'ici. Un autre code spécifique au projet a été supprimé par souci de concision:

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

Le problème réside dans le fait que je dois maintenant transmettre des données dynamiques de Node et les compiler comme une variable SASS normale. Au départ, j’ai essayé d’utiliser PostCSS , car j’ai remarqué que l’injection variable était quelque chose qu’elle pouvait faire . Malheureusement, cela n'a pas fonctionné. PostCSS débute après la phase de compilation, qui échoue lamentablement à ce stade.

Ensuite, j'ai essayé d'utiliser underscore templates pour essayer de remplacer en utilisant 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' })
            };
        }
    });

Ce qui a entraîné l'erreur suivante:

Error: error reading values after :

De toute évidence, SASS n'a pas aimé la syntaxe variable du trait de soulignement.


TL; DR

Comment puis-je transmettre des variables dynamiques à SASS à partir de mon application Node?


Information additionnelle

  1. Mon équipe et moi n’avons pas tout à fait peur de passer à quelque chose du type Stylus ; Cependant, nous avons réalisé des progrès importants jusqu'à présent et ce serait difficile.
22
Chris Wright

Je me suis retrouvé dans une situation très similaire. Nous avions beaucoup de SASS existants qui devaient maintenant accepter l'utilisation de valeurs/variables dynamiques (en tant que variables). À l’origine, j’écris des répertoires/fichiers temporaires et crée essentiellement un "point d’entrée proxy" qui créerait un proxy_entry.scss et un variables.scss et amorcerait le entry.scss avec les variables SASS prévues déclarées. Cela a bien fonctionné et a permis d'obtenir les résultats souhaités, mais cela semblait un peu compliqué ...

Il s'avère qu'il existe une solution beaucoup plus simple disponible grâce à node-sass's options.data . Ceci accepte une "chaîne SASS à évaluer". 

Type: chaîne Valeur par défaut: null Spécial: le fichier ou les données doivent être spécifiés

Une chaîne à transmettre à libsass à rendre. Il est recommandé d'utiliser includePaths en conjonction avec this afin que libsass puisse rechercher des fichiers lors de l'utilisation de la directive @import.

Cela a complètement éliminé le besoin d'écrire/gérer tous les répertoires et fichiers temporaires. 

Visual TL; DR

 Dynamic Variables in SASS with node-sass

La solution se résume à quelque chose comme ça

1.) Définir sassOptions comme d'habitude

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

2.) Écrivez la "chaîne SASS dynamique" pour options.data

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

3.) Évaluer le SASS comme d'habitude

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

Note:Ceci suppose que votre entry.scss importe un variables.scss qui définit les variables comme "par défaut".

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

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

Assembler tout cela à titre d'exemple

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

Où les fonctions "sassGenerator" pourraient ressembler

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 + "';";
}

Cela vous permet d’écrire votre SASS exactement comme vous l’avez fait auparavant, en utilisant des variables SASS partout où elles sont nécessaires. Cela ne vous lie pas non plus à une "implémentation dynamique spéciale" (c’est-à-dire que cela évite d’utiliser des "styles de soulignement/lodash dans tous vos fichiers .scss). Cela signifie également que vous pouvez tirer parti des fonctionnalités de IDE, le linting, etc. ... de la même façon puisque vous êtes maintenant juste retournez à l'écriture de SASS normal.

De plus, cela se traduit joliment par des utilisations autres que les noeuds/http/compilées à la volée, telles que la précompilation de plusieurs variantes de entry.scss à partir de plusieurs ensembles de valeurs via Gulp, etc.

J'espère que cela vous aide @ChrisWright (et d'autres) à sortir! Je sais que j'ai eu du mal à trouver des informations sur le sujet et j'imagine qu'il s'agit d'un cas d'utilisation assez courant (vouloir transmettre des valeurs dynamiques au SASS à partir d'une base de données, d'une configuration, de paramètres HTTP, etc.).

58
Erik Aybar

J'ai pu résoudre ce problème après avoir enveloppé ma tête autour de la méthode node-sass ' importer() . Ma solution consistait à souligner les modèles et à lire manuellement les fichiers au fur et à mesure de leur arrivée. Ce n'est pas la solution la plus élégante ni la plus efficace, mais elle ne s'exécute qu'une fois par page générée. Après cela, les fichiers sont minifiés et mis en cache pour les demandes futures.

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

En utilisant cette solution, nous sommes en mesure de référencer la syntaxe normale des variables de soulignement dans nos partiels SCSS. Par exemple:

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

Je résolvais un problème similaire mais pas dans Node mais en Java. Je devais rendre les variables SASS de la base de données pour générer le thème du site Web, en fonction du client accédant au site Web.

J'ai exploré quelques solutions et suis tombé sur ce service tiers https://www.grooveui.com . Il offre une solution indépendante de la langue pour résoudre ce problème.

0
Anubhav