tools/compile-css.mjs (100 lines of code) (raw):
// unified CSS creation module. used for prod and dev/watch tasks.
// writes files to `static/target/stylesheets` i.e. has side-effects
// exports a function which takes:
// 1. glob for files in static/src/stylesheets
// 2. options object offering `remify` (boolean) and `browsers` (browserlist)
import fs from 'node:fs';
import path from 'node:path';
import mkdirp from 'mkdirp';
import glob from 'glob';
import pify from 'pify';
import * as sass from 'sass';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
import pxtorem from 'postcss-pxtorem';
const sassRenderP = pify(sass.render);
const writeFileP = pify(fs.writeFile);
import { paths } from './__tasks__/config.mjs';
const sassDir = path.resolve(paths.src, 'stylesheets');
const SASS_SETTINGS = {
outputStyle: 'compressed',
sourceMap: true,
precision: 5,
quietDeps: true,
silenceDeprecations: ['mixed-decls'],
};
const BROWSERS_LIST = [
'Firefox >= 45',
'Explorer >= 10',
'Safari >= 7',
'Chrome >= 50',
'iOS >= 7',
'Android >= 5',
'BlackBerry >= 10',
'ExplorerMobile >= 10',
'> 2% in US',
'> 2% in AU',
'> 2% in GB',
];
const REMIFICATIONS = {
replace: true,
root_value: 16,
unit_precision: 5,
propList: ['*'],
};
/** @param {string} sassGlob */
const getFiles = (sassGlob) => glob.sync(path.resolve(sassDir, sassGlob));
/**
* @param {string} sassGlob
* @param {object} [options]
* @param {boolean} options.remify
* @param {string[]} options.browsers
*/
export const compileSass = (sassGlob, { remify = true, browsers = BROWSERS_LIST } = {}) => {
if (typeof sassGlob !== 'string') {
return Promise.reject(new Error('No glob provided.'));
}
return Promise.all(
getFiles(sassGlob).map((filePath) => {
const dest = path.resolve(
paths.target,
'stylesheets',
path.relative(sassDir, filePath).replace('scss', 'css'),
);
const sassOptions = Object.assign(
{
file: filePath,
outFile: dest,
sourceMapContents: true,
includePaths: ['node_modules'],
},
SASS_SETTINGS,
);
const postcssPlugins = [
autoprefixer({ overrideBrowserslist: browsers }),
];
if (remify) {
postcssPlugins.push(pxtorem(REMIFICATIONS));
}
mkdirp.sync(path.parse(dest).dir);
return sassRenderP(sassOptions)
.then((result) =>
postcss(postcssPlugins).process(result.css.toString(), {
from: filePath,
to: dest,
map: {
inline: false,
prev: result.map.toString(),
},
}),
)
.then((result) =>
Promise.all([
writeFileP(dest, result.css.toString()),
writeFileP(`${dest}.map`, result.map.toString()),
]),
);
}),
);
};
export default compileSass