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.
Comment puis-je transmettre des variables dynamiques à SASS à partir de mon application Node?
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.
La solution se résume à quelque chose comme ça
var sassOptionsDefaults = {
includePaths: [
'some/include/path'
],
outputStyle: 'compressed'
};
options.data
var dataString =
sassGenerator.sassVariables(variables) +
sassGenerator.sassImport(scssEntry);
var sassOptions = _.assign({}, sassOptionsDefaults, {
data: dataString
});
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;
}
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.).
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 %>;
}
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.