Files
OfficialSite/build/tasks/styles.js

215 lines
7.3 KiB
JavaScript
Raw Normal View History

import loconfig from '../../loconfig.json';
import message from '../utils/message.js';
import notification from '../utils/notification.js';
import postcss, { pluginsMap as postcssPluginsMap } from '../utils/postcss.js';
import resolve from '../utils/template.js';
import { writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { promisify } from 'node:util';
import sass from 'node-sass';
const sassRender = promisify(sass.render);
let postcssProcessor;
/**
* @const {object} defaultSassOptions - The default shared Sass options.
* @const {object} developmentSassOptions - The predefined Sass options for development.
* @const {object} productionSassOptions - The predefined Sass options for production.
*/
export const defaultSassOptions = {
omitSourceMapUrl: true,
sourceMap: true,
sourceMapContents: true,
};
export const developmentSassOptions = Object.assign({}, defaultSassOptions, {
outputStyle: 'expanded',
});
export const productionSassOptions = Object.assign({}, defaultSassOptions, {
outputStyle: 'compressed',
});
/**
* @const {object} defaultPostCSSOptions - The default shared PostCSS options.
* @const {object} developmentPostCSSOptions - The predefined PostCSS options for development.
* @const {object} productionPostCSSOptions - The predefined PostCSS options for production.
*/
export const defaultPostCSSOptions = {
processor: {
map: {
annotation: false,
inline: false,
sourcesContent: true,
},
},
};
export const developmentPostCSSOptions = Object.assign({}, defaultPostCSSOptions);
export const productionPostCSSOptions = Object.assign({}, defaultPostCSSOptions);
/**
2021-11-03 17:08:38 -04:00
* @const {object|boolean} developmentStylesArgs - The predefined `compileStyles()` options for development.
* @const {object|boolean} productionStylesArgs - The predefined `compileStyles()` options for production.
*/
export const developmentStylesArgs = [
developmentSassOptions,
developmentPostCSSOptions,
];
export const productionStylesArgs = [
productionSassOptions,
productionPostCSSOptions,
];
/**
* Compiles and minifies main Sass files to CSS.
2021-09-21 16:01:47 -04:00
*
* @todo Add deep merge of `postcssOptions` to better support customization
* of default processor options.
*
2021-09-21 16:01:47 -04:00
* @async
* @param {object} [sassOptions=null] - Customize the Sass render API options.
* If `null`, default production options are used.
* @param {object|boolean} [postcssOptions=null] - Customize the PostCSS processor API options.
* If `null`, default production options are used.
* If `false`, PostCSS processing will be ignored.
2021-09-21 16:01:47 -04:00
* @return {Promise}
*/
export default async function compileStyles(sassOptions = null, postcssOptions = null) {
if (sassOptions == null) {
sassOptions = productionSassOptions;
} else if (
sassOptions !== developmentSassOptions &&
sassOptions !== productionSassOptions
) {
sassOptions = Object.assign({}, defaultSassOptions, sassOptions);
}
if (postcss) {
if (postcssOptions == null) {
postcssOptions = productionPostCSSOptions;
} else if (
postcssOptions !== false &&
postcssOptions !== developmentPostCSSOptions &&
postcssOptions !== productionPostCSSOptions
) {
postcssOptions = Object.assign({}, defaultPostCSSOptions, postcssOptions);
}
}
loconfig.tasks.styles.forEach(async ({
infile,
outfile,
label = null
}) => {
const filestem = basename((outfile || 'undefined'), '.css');
const timeLabel = `${label || `${filestem}.css`} compiled in`;
console.time(timeLabel);
2020-12-11 16:00:36 -05:00
try {
infile = resolve(infile);
outfile = resolve(outfile);
let result = await sassRender(Object.assign({}, sassOptions, {
file: infile,
outFile: outfile,
}));
if (postcss && postcssOptions) {
if (typeof postcssProcessor === 'undefined') {
postcssProcessor = createPostCSSProcessor(
postcssPluginsMap,
postcssOptions
);
}
result = await postcssProcessor.process(
result.css,
Object.assign({}, postcssOptions.processor, {
from: outfile,
to: outfile,
})
);
if (result.warnings) {
const warnings = result.warnings();
if (warnings.length) {
message(`Error processing ${label || `${filestem}.css`}`, 'warning');
warnings.forEach((warn) => {
message(warn.toString());
});
}
}
}
try {
await writeFile(outfile, result.css);
if (result.css) {
message(`${label || `${filestem}.css`} compiled`, 'success', timeLabel);
} else {
message(`${label || `${filestem}.css`} is empty`, 'notice', timeLabel);
}
} catch (err) {
message(`Error compiling ${label || `${filestem}.css`}`, 'error');
message(err);
notification({
title: `${label || `${filestem}.css`} save failed 🚨`,
message: `Could not save stylesheet to ${label || `${filestem}.css`}`
});
}
if (result.map) {
try {
await writeFile(outfile + '.map', result.map.toString());
} catch (err) {
message(`Error compiling ${label || `${filestem}.css.map`}`, 'error');
message(err);
notification({
title: `${label || `${filestem}.css.map`} save failed 🚨`,
message: `Could not save sourcemap to ${label || `${filestem}.css.map`}`
});
}
}
} catch (err) {
message(`Error compiling ${label || `${filestem}.scss`}`, 'error');
message(err.formatted || err);
notification({
title: `${label || `${filestem}.scss`} compilation failed 🚨`,
message: (err.formatted || `${err.name}: ${err.message}`)
});
}
});
};
/**
* Creates a PostCSS Processor with the given plugins and options.
*
* @param {array<(function|object)>|object<string, (function|object)>} pluginsListOrMap -
* A list or map of plugins.
* If a map of plugins, the plugin name looks up `options`.
* @param {object} options - The PostCSS options.
*/
function createPostCSSProcessor(pluginsListOrMap, options)
{
let plugins;
if (Array.isArray(pluginsListOrMap)) {
plugins = pluginsListOrMap;
} else {
plugins = [];
for (let [ name, plugin ] of Object.entries(pluginsListOrMap)) {
if (name in options) {
plugin = plugin[name](options[name]);
}
plugins.push(plugin);
}
}
return postcss(plugins);
}