UNPKG

deployment-tools

Version:

A Node.js scripts that helps you to compile and deploy the static assets (CSS/JavaScript/images) of your website without gulp and grunt using Node.js and npm scripts

163 lines (144 loc) 7.2 kB
import fs from '../libs/fs'; import logger from '../libs/logger'; const debug = require('debug')('dt'); const path = require('path'); const postcss = require('postcss'); const postcssDevtools = require('postcss-devtools'); const postcssAtImport = require('postcss-import'); const postcssAutoprefixer = require('autoprefixer'); const postcssUrl = require('postcss-url'); const postcssCalc = require('postcss-calc'); const postcssReporter = require('postcss-reporter'); const postcssStylelint = require('stylelint'); const postcssClean = require('postcss-clean'); const mdcss = require('mdcss'); const doiuse = require('doiuse'); const crypto = require('crypto'); const trimEnd = require('lodash/trimEnd'); /** * Write minified version of the file * @param {object} [obj] - obj * @param {string} obj.filename - * @param {string} obj.styledocPath - * @param {string} obj.compiledCss - * @param {string} obj.outputFolder - * @return {Promise} A Promise */ async function generateMinifiedAsync({ filename, styledocPath, compiledCss, outputFolder }) { // write in parallel minified version logger.log(`minify stylesheet: filename:${filename}`); // minify via clean-css const minPlugins = []; // generate css documentation?? if (styledocPath) { const pathDoc = `${trimEnd(styledocPath, '/')}_${filename}`; debug(`[CSS] Generate documentation for ${filename} pathDoc: ${pathDoc}`); minPlugins.push(mdcss({ destination: pathDoc, assets: [] })); // assets: The list of files or directories to copy into the style guide directory. } // add css-clean plugin for minify minPlugins.push(postcssClean()); const processedMinCssObject = await postcss(minPlugins).process(compiledCss.css); // compute the hash(md5) of the file const filehash = crypto.createHash('md5').update(processedMinCssObject.css, 'utf8').digest('hex'); await fs.writeAsync(`${outputFolder}${filename}.${filehash}.css`, processedMinCssObject.css); // generate map file for min ??? if (processedMinCssObject.map) await fs.writeAsync(`${outputFolder}${filehash}.css.map`, processedMinCssObject.map); return { filename: `${filename}.css`, filehash: `${filehash}.css` }; } /** * Build, lint and minify css. * @param {object} [obj] - obj * @param {string} obj.srcFolder - The folder that contains the file * @param {string} obj.filename - The name of the file (main.less/main.sass) * @param {string} obj.outputFolder - The output folder * @param {string} obj.cdnDomain - The domain (with http://) of the CDN for images * @param {boolean} obj.minify=false - Compile a minified version of the file * @param {string} obj.engine - Which engine to use less/sass * @param {string} obj.projectName - The name of the project * @param {string} [obj.stylelintrc] - Relative path of the configuration file for stylelint * @param {string} obj.styledocPath - Relative path for the documentation output * @param {string} obj.doiuseRules='' - Browser to check with doiuse * @param {string} obj.autoprefixerRules='' - Browser to autoprefix * @return {Promise} A Promise */ async function compileStylesheetAsync({ srcFolder, outputFolder, filename, cdnDomain, minify = false, projectName, engine, stylelintrc, styledocPath, doiuseRules = '', autoprefixerRules = '' }) { const filepath = `${srcFolder}${filename}`; const filesPath = []; // add the folder with the less files filesPath.push(path.join(process.cwd(), 'node_modules')); filesPath.push(path.join(process.cwd(), srcFolder)); logger.log(`compile less srcFolder:${srcFolder} outputFolder:${outputFolder} filename:${filename} minify:${minify} path: ${filesPath}`); // read less/sass file const source = await fs.readFileAsync(filepath); const ext = path.extname(filename); // es: main.less-> main const cssOutputFileName = path.basename(filename, ext); // css/main.css const cssOutputPath = `${outputFolder}${cssOutputFileName}.css`; const cssMapOutputPath = `${outputFolder}${cssOutputFileName}.css.map`; logger.log(`compile stylesheet: write output at cssOutputPath:${cssOutputPath} filename:${filename}`); // PLUGINS: prepare plugins for postCss const postCssPlugins = []; postCssPlugins.push(postcssDevtools()); // USE stylint ?? if (stylelintrc) { const stylelintrcFile = path.join(process.cwd(), stylelintrc); debug(`[CSS] Enable stylint on file:${filename} with file ${stylelintrcFile} and cwd ${process.cwd()}`); postCssPlugins.push(postcssStylelint({ configFile: stylelintrcFile })); } postCssPlugins.push(engine({ strictMath: true, paths: filesPath })); postCssPlugins.push(postcssAtImport({ path: ['data/less'] })); // USE AUTOPREFIXER ?? const autoprefixerRulesArr = autoprefixerRules.split(','); if (autoprefixerRulesArr.length) { debug(`[CSS] Enable autoprefixer on file:${filename} with ${autoprefixerRulesArr}`); postCssPlugins.push(postcssAutoprefixer({ browsers: autoprefixerRulesArr })); } // transform image url for CDN postCssPlugins.push(postcssUrl({ url(imageurl) { if (minify) { if (!imageurl.startsWith('http')) { const newImageUrl = imageurl.startsWith('/') ? `${cdnDomain}/${projectName}${imageurl}` : imageurl; debug(`[CSS] Found inside ${filename} an image url original:${imageurl} new:${newImageUrl}`); return newImageUrl; } } return imageurl; }, })); // CAN I USE ?? const doiuseRulesArr = doiuseRules.split(','); if (doiuseRulesArr.length) { debug(`[CSS] Enable doiuse on file:${filename} with ${doiuseRulesArr}`); postCssPlugins.push(doiuse({ browsers: [], ignore: [], // an optional array of features to ignore 'rem' ignoreFiles: [path.join(process.cwd(), 'node_modules', `/**/*${ext}`)], // an optional array of file globs to match against original source file path, to ignore // onFeatureUsage(usageInfo) { // logger.warn('CANIUSE', usageInfo.message); // }, })); } // use postcss calc plugin postCssPlugins.push(postcssCalc()); postCssPlugins.push(postcssReporter({ clearMessages: true })); // clearMessages if true, the plugin will clear the result's messages after it logs them // postcss(plugins) list of PostCSS plugins to be included as processors. const cssProcessor = postcss(postCssPlugins); // process less file debug(`[CSS] Compiling stylesheet file:${filename}`); const processedCssObject = await cssProcessor.process(source, { parser: engine.parser, from: filepath }); debug(`[CSS] Compiled stylesheet file:${filename}`); // write css output on file const outputTask = []; outputTask.push(fs.writeAsync(cssOutputPath, processedCssObject.css)); // generate map file for unminified file ??? if (processedCssObject.map) outputTask.push(fs.writeAsync(cssMapOutputPath, processedCssObject.map)); // enable minify (and css docs)??? if (minify) { outputTask.push(generateMinifiedAsync({ filename: cssOutputFileName, styledocPath, outputFolder, compiledCss: processedCssObject })); } debug(`[CSS] Running ${outputTask.length} task(s) in parallel`); return Promise.all(outputTask); } export default compileStylesheetAsync;