licss
Version:
A Gulp plugin for processing styles with support for .css, .scss, .sass, and .pcss files. It uses minification, autoprefixing, and removes unused CSS styles.
248 lines (247 loc) • 10.1 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
//#region \0rolldown/runtime.js
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
let node_buffer = require("node:buffer");
let node_path = require("node:path");
let node_stream = require("node:stream");
let node_url = require("node:url");
let browserslist = require("browserslist");
browserslist = __toESM(browserslist);
let colors = require("colors");
colors = __toESM(colors);
let fancy_log = require("fancy-log");
fancy_log = __toESM(fancy_log);
let lightningcss = require("lightningcss");
let plugin_error = require("plugin-error");
plugin_error = __toESM(plugin_error);
let purgecss = require("purgecss");
let sass_embedded = require("sass-embedded");
let tinyglobby = require("tinyglobby");
//#region src/licss.ts
const textDecoder = new TextDecoder();
/**
* Gulp plugin for style transformation - bundles, compiles, minimizes, and cleans up sass, scss, css, and postcss style sheets.
* @param minify use LightningCSS for minify CSS files
* @param loadPaths paths for files to imports for SASS/SCSS compiler
* @param purgeCSSoptions remove unused CSS from file - options PurgeCSS
* @param verbose display more messages
* @returns Transform stream.
*
* @example
*
* ```js
* // import modules
* import { dest, src } from 'gulp'
* import { licss, rename } from 'licss'
*
* // sample task for postcss files
* function styles() {
* return src(['src/styles/main.css'], { sourcemaps: true })
* .pipe(licss({
* purgeCSSoptions: {
* content: ["src/*.html", "src/scripts/*.ts"],
* },
* }))
* .pipe(rename({ suffix: '.min', extname: '.css' }))
* .pipe(dest('dist/css', { sourcemaps: '.' })) // for file sourcemap
* // or .. { sourcemaps: true } for inline soutcemap
* }
*
* // export
* export { styles }
*
* ```
*/
function licss({ minify = true, loadPaths, purgeCSSoptions, verbose = false } = {}) {
const stream = new node_stream.Transform({ objectMode: true });
stream._transform = async (file, _enc, cb) => {
if (file.isNull()) return cb(null, file);
if (file.isStream()) return cb(new plugin_error.default("licss", "Streams are not supported"));
if (file.stem.startsWith("_")) return cb();
if (file.isBuffer()) try {
if (!loadPaths) loadPaths = [(0, node_path.dirname)(file.path), (0, node_path.join)(file.cwd, "node_modules")];
const extname = file.extname.split(".").pop()?.toLowerCase() ?? "";
if (!/^(css|scss|sass|pcss|postcss)$/i.test(extname)) throw new Error("• \"licss\": Unsupported file extension. Supported: .css, .scss, .sass, .pcss");
const targetsList = getTargets(file.cwd);
const isPcssFile = /^(pcss|postcss)$/i.test(extname);
const isPurge = !!purgeCSSoptions;
const isSourceMap = file.sourceMap ? true : false;
if (verbose) mess(`Options: minify: ${minify}, sourcemap: ${isSourceMap}, purge: ${isPurge}, file:`, file);
if (isPcssFile) {
if (verbose) mess("Run bundle LightningCSS for file:", file);
bundleLightningCSS(file, isSourceMap);
} else {
if (verbose) mess("Run SASS compiler for file:", file);
await bundleSASS(file, loadPaths, isSourceMap);
}
if (isPurge) {
if (verbose) mess("Purge unused CSS in file:", file);
await purgeTransform(file, purgeCSSoptions);
}
if (!purgeCSSoptions?.rejected) {
if (verbose) mess("Run transform LightningCSS for file:", file);
transformLightningCSS(file, minify, targetsList, isSourceMap, isPurge);
}
cb(null, file);
} catch (err) {
throw new plugin_error.default("licss", err instanceof Error ? err : new Error(String(err)), {
fileName: file.path,
showStack: true
});
}
else throw new plugin_error.default("licss", { message: "File not found!" });
};
return stream;
}
function mess(message, file) {
return (0, fancy_log.default)(colors.default.cyan("licss ") + colors.default.red("★ ") + colors.default.magenta(message + " ") + colors.default.blue(file.relative));
}
function getTargets(cwd) {
return (0, lightningcss.browserslistToTargets)((0, browserslist.default)(browserslist.default.loadConfig({ path: cwd }) ?? (0, browserslist.default)("> 0.2%, last 2 major versions, not dead")));
}
function transformLightningCSS(file, minify, targetsList, isSourceMap, isPurge) {
if (file.isBuffer()) {
const result = (0, lightningcss.transform)({
filename: file.path,
minify,
code: file.contents,
sourceMap: isSourceMap,
inputSourceMap: isPurge ? void 0 : isSourceMap ? JSON.stringify(file.sourceMap) : void 0,
projectRoot: file.cwd,
targets: minify ? targetsList : void 0
});
file.extname = ".css";
file.contents = node_buffer.Buffer.from(result.code);
if (result.map) file.sourceMap = JSON.parse(textDecoder.decode(result.map));
} else throw new Error("transformLightningCSS: File not found!");
}
function bundleLightningCSS(file, isSourceMap) {
if (file.isBuffer()) {
const result = (0, lightningcss.bundle)({
filename: file.path,
minify: false,
sourceMap: isSourceMap,
inputSourceMap: void 0,
projectRoot: file.cwd,
targets: void 0
});
file.extname = ".css";
file.contents = node_buffer.Buffer.from(result.code);
if (result.map) file.sourceMap = JSON.parse(textDecoder.decode(result.map));
} else throw new Error("bundleLightningCSS: File not found!");
}
async function bundleSASS(file, loadPaths, isSourceMap) {
if (file.isBuffer()) try {
const result = await (0, sass_embedded.compileStringAsync)(parseImport(file), {
url: (0, node_url.pathToFileURL)(file.path),
loadPaths,
syntax: file.extname === ".sass" ? "indented" : "scss",
sourceMap: isSourceMap,
sourceMapIncludeSources: true,
silenceDeprecations: [
"import",
"color-functions",
"global-builtin",
"legacy-js-api",
"if-function"
]
});
file.extname = ".css";
file.contents = node_buffer.Buffer.from(result.css);
if (result.sourceMap && isSourceMap) cleanSourceMap(file, JSON.parse(JSON.stringify(result.sourceMap)));
} catch (err) {
throw new plugin_error.default("licss", err instanceof Error ? err : new Error(String(err)), {
message: "Error! Sass compiler: ",
fileName: file.path,
showStack: true
});
}
else throw new Error("bundleSASS: File not found!");
}
function cleanSourceMap(file, map) {
const basePath = file.base;
map.file = file.relative.replace(/\\/g, "/");
map.sources = map.sources.map((path) => {
if (path.startsWith("file:")) path = (0, node_url.fileURLToPath)(path);
path = (0, node_path.relative)(basePath, path);
return path.replace(/\\/g, "/");
});
file.sourceMap = map;
}
async function getFiles(contentArray, ignore) {
return await (0, tinyglobby.glob)(contentArray, { ignore });
}
function parseImport(file) {
if (file.isBuffer()) return textDecoder.decode(file.contents).replace(/@import +([url(]*)["']([./]*)([a-z-_/]+)\.?(.*)['"]\)?/gi, "@import \"$2$3\"");
else throw new Error("parseImport: File not found!");
}
async function purgeTransform(file, options) {
if (!options || typeof options !== "object" || Array.isArray(options)) throw new Error("Error! Check the type PurgeCSS options.");
if (!options.content || !Array.isArray(options.content) || options.content.length === 0) throw new Error("Error! PurgeCSS requires a non-empty \"content\" array.");
if (file.isBuffer()) {
const processedContent = await getFiles(options.content, options.skippedContentGlobs);
const purge = (await new purgecss.PurgeCSS().purge({
...options,
content: processedContent,
css: [{ raw: textDecoder.decode(file.contents) }],
stdin: true,
sourceMap: false
}))[0];
const rejected = options.rejected && purge.rejected && purge.rejected.length > 0 ? purge.rejected.join(" {}\n") + " {}" : "";
const result = rejected ? rejected : purge.css;
file.contents = node_buffer.Buffer.from(result, "utf8");
} else throw new Error("purgeTransform: File not found!");
}
/**
* Gulp plugin for rename file - change extname or/and added suffix
* @param basename - new file name (file stem and file extension)
* @param extname - new file extension
* @param suffix - new file suffix
*/
function rename({ basename = void 0, extname = void 0, suffix = void 0 } = {}) {
const stream = new node_stream.Transform({ objectMode: true });
stream._transform = async (sameFile, _enc, callback) => {
if (sameFile.isNull()) return callback(null, sameFile);
if (sameFile.isStream()) return callback(new plugin_error.default("licss", "Streams are not supported"));
if (sameFile.isBuffer()) try {
const file = sameFile.clone({ contents: false });
if (basename) file.basename = basename;
const extName = file.extname;
if (suffix && extname) file.extname = suffix + extname;
else if (suffix && !extname) file.extname = suffix + extName;
else if (!suffix && extname) file.extname = extname;
if (file.sourceMap) file.sourceMap.file = file.relative;
callback(null, file);
} catch (err) {
throw new plugin_error.default("licss", err instanceof Error ? err : new Error(String(err)), {
fileName: sameFile.path,
showStack: true
});
}
else callback(null, sameFile);
};
return stream;
}
//#endregion
exports.licss = licss;
exports.rename = rename;