UNPKG

vite-plugin-pretty-module-classnames

Version:

Make your scoped CSS module class names clear and readable — this plugin automatically adds the module filename and other useful info to class names for easier development.

122 lines (117 loc) 5.42 kB
"use strict"; //#region rolldown:runtime 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 const crypto = __toESM(require("crypto")); //#region src/constants.ts const GENERATE_SCOPED_NAME_WARNING = "[vite-plugin-pretty-module-classnames]:: The 'generateScopedName' configuration has already been set. Your vite.config configuration or other plugins might be attempting to override this setting, which could affect the proper functioning of vite-plugin-pretty-module-classnames."; const defaultOptions = { lineNumber: false, separator: { beforeHash: "_", beforeClassName: "__", beforeLineNumber: "-" } }; //#endregion //#region src/utils.ts function getHash(input) { return (0, crypto.createHash)("sha256").update(input).digest("hex").slice(0, 5); } function getLineNumber(cssData, className) { const lines = cssData.split("\n"); const match = new RegExp(`\\.${className}\\b`); return lines.findIndex((line) => match.test(line)) + 1; } function deepMerge(defaultOptions$1, userOptions) { const result = { ...defaultOptions$1 }; for (const key in userOptions) { const value = userOptions[key]; if (value != null && typeof value === "object" && !Array.isArray(value)) result[key] = deepMerge(defaultOptions$1[key], value); else if (value !== void 0) result[key] = value; } return result; } /** * Generates a unique class name for CSS modules based on the file name and the specified class name. * The file name is processed to remove specific parts such as ".module" and file extensions. * If `lineNumber` is provided, it's added to the generated class name. * * @param name - The name of the CSS class that will be part of the generated unique name. * @param filename is the full path to the style file, which can be undefined. If the parameter is undefined, the function throws an exception. * @param lineNumber is the line number where the CSS class is defined, optional. * @returns A string with a unique class name, including a sanitized file name, the original class name, a randomly generated hash, and optionally the line number. * @throws Error if `filename` is not provided or is not a string. */ function sanitizeModuleClassname(name, filename, separator, lineNumber) { if (typeof filename !== "string") throw new Error("The filename must be string and cannot be undefined."); const parts = filename.split("?")[0].split("/"); const lastSegment = parts.pop(); if (!lastSegment) throw new Error("Filename must include a valid file name."); const baseFilename = lastSegment.replace(/(\.vue|\.module)?(\.\w+)$/, ""); const pathHash = getHash(parts.join("/")); const classname = `${baseFilename}${separator.beforeClassName}${name}`; const hash = `${separator.beforeHash}${getHash(`${pathHash}-${classname}`)}`; const lineInfo = lineNumber !== void 0 ? `${separator.beforeLineNumber}${lineNumber}` : ""; return `${classname}${hash}${lineInfo}`; } //#endregion //#region src/index.ts /** * Adds the filename without the `-module` suffix to the class names of CSS modules. * It customizes the generateScopedName function to use a sanitized version of the filename, class name, and a hash. * If the `lineNumber` option is set to true, the line number is added to the generated class name. * * @prop {Object} `options` - Plugin options. * @prop {boolean} `options.lineNumber` - Whether to include the line number in the generated class name. @default false * @prop {string} `options.separator.beforeHash` - @default '_' * @prop {string} `options.separator.beforeClassName` - @default '__' * @prop {string} `options.separator.beforeLineNumber` - @default '-' * @returns {Plugin} A Vite plugin object with a custom configuration for CSS modules. */ function prettyModuleClassnames(userOptions = {}) { return { name: "vite-plugin-pretty-module-classnames", config(config) { const options = deepMerge(defaultOptions, userOptions); const cssModules = config.css?.modules; if (process.env.VITEST) return {}; if (cssModules && "generateScopedName" in cssModules && cssModules.generateScopedName) console.warn(GENERATE_SCOPED_NAME_WARNING); const newCssConfig = { ...config.css, modules: { ...cssModules, generateScopedName: (name, filename, css) => { const lineNumber = options.lineNumber ? getLineNumber(css, name) : void 0; return sanitizeModuleClassname(name, filename, options.separator, lineNumber); } } }; return { ...config, css: newCssConfig }; } }; } //#endregion module.exports = prettyModuleClassnames;