UNPKG

less

Version:
1,501 lines (1,337 loc) 559 kB
/** * Less - Leaner CSS v4.6.4 * http://lesscss.org * * Copyright (c) 2009-2026, Alexis Sellier <self@cloudhead.net> * Licensed under the Apache-2.0 License. * * @license Apache-2.0 */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.less = factory()); })(this, (function () { 'use strict'; // Export a new default each time function defaultOptions() { return { /* Inline Javascript - @plugin still allowed */ javascriptEnabled: false, /* Outputs a makefile import dependency list to stdout. */ depends: false, /* (DEPRECATED) Compress using less built-in compression. * This does an okay job but does not utilise all the tricks of * dedicated css compression. */ compress: false, /* Runs the less parser and just reports errors without any output. */ lint: false, /* Sets available include paths. * If the file in an @import rule does not exist at that exact location, * less will look for it at the location(s) passed to this option. * You might use this for instance to specify a path to a library which * you want to be referenced simply and relatively in the less files. */ paths: [], /* color output in the terminal */ color: true, /** * @deprecated This option has confusing behavior and may be removed in a future version. * * When true, prevents @import statements for .less files from being evaluated inside * selector blocks (rulesets). The imports are silently ignored and not output. * * Behavior: * - @import at root level: Always processed * - @import inside @-rules (@media, @supports, etc.): Processed (these are not selector blocks) * - @import inside selector blocks (.class, #id, etc.): NOT processed (silently ignored) * * When false (default): All @import statements are processed regardless of context. * * Note: Despite the name "strict", this option does NOT throw an error when imports * are used in selector blocks - it silently ignores them. This is confusing * behavior that may catch users off guard. * * Note: Only affects .less file imports. CSS imports (url(...) or .css files) are * always output as CSS @import statements regardless of this setting. * * @see https://github.com/less/less.js/issues/656 */ strictImports: false, /* Allow Imports from Insecure HTTPS Hosts */ insecure: false, /* Allows you to add a path to every generated import and url in your css. * This does not affect less import statements that are processed, just ones * that are left in the output css. */ rootpath: '', /* By default URLs are kept as-is, so if you import a file in a sub-directory * that references an image, exactly the same URL will be output in the css. * This option allows you to re-write URL's in imported files so that the * URL is always relative to the base imported file */ rewriteUrls: false, /* How to process math * 0 always - eagerly try to solve all operations * 1 parens-division - require parens for division "/" * 2 parens | strict - require parens for all operations * 3 strict-legacy - legacy strict behavior (super-strict) */ math: 1, /* Without this option, less attempts to guess at the output unit when it does maths. */ strictUnits: false, /* Effectively the declaration is put at the top of your base Less file, * meaning it can be used but it also can be overridden if this variable * is defined in the file. */ globalVars: null, /* As opposed to the global variable option, this puts the declaration at the * end of your base file, meaning it will override anything defined in your Less file. */ modifyVars: null, /* This option allows you to specify a argument to go on to every URL. */ urlArgs: '' } } /** @param {string} href */ function extractId(href) { return href.replace(/^[a-z-]+:\/+?[^/]+/, '') // Remove protocol & domain .replace(/[?&]livereload=\w+/, '') // Remove LiveReload cachebuster .replace(/^\//, '') // Remove root / .replace(/\.[a-zA-Z]+$/, '') // Remove simple extension .replace(/[^.\w-]+/g, '-') // Replace illegal characters .replace(/\./g, ':'); // Replace dots with colons(for valid id) } /** * @param {Record<string, *>} options * @param {HTMLElement | null} tag */ function addDataAttr(options, tag) { if (!tag) {return;} // in case of tag is null or undefined for (const opt in tag.dataset) { if (Object.prototype.hasOwnProperty.call(tag.dataset, opt)) { if (opt === 'env' || opt === 'dumpLineNumbers' || opt === 'rootpath' || opt === 'errorReporting') { options[opt] = tag.dataset[opt]; } else { try { options[opt] = JSON.parse(tag.dataset[opt]); } catch (_) {} } } } } var browser$1 = { createCSS: function (document, styles, sheet) { // Strip the query-string const href = sheet.href || ''; // If there is no title set, use the filename, minus the extension const id = `less:${sheet.title || extractId(href)}`; // If this has already been inserted into the DOM, we may need to replace it const oldStyleNode = document.getElementById(id); let keepOldStyleNode = false; // Create a new stylesheet node for insertion or (if necessary) replacement const styleNode = document.createElement('style'); styleNode.setAttribute('type', 'text/css'); if (sheet.media) { styleNode.setAttribute('media', sheet.media); } styleNode.id = id; if (!styleNode.styleSheet) { styleNode.appendChild(document.createTextNode(styles)); // If new contents match contents of oldStyleNode, don't replace oldStyleNode keepOldStyleNode = (oldStyleNode !== null && oldStyleNode.childNodes.length > 0 && styleNode.childNodes.length > 0 && oldStyleNode.firstChild.nodeValue === styleNode.firstChild.nodeValue); } const head = document.getElementsByTagName('head')[0]; // If there is no oldStyleNode, just append; otherwise, only append if we need // to replace oldStyleNode with an updated stylesheet if (oldStyleNode === null || keepOldStyleNode === false) { const nextEl = sheet && sheet.nextSibling || null; if (nextEl) { nextEl.parentNode.insertBefore(styleNode, nextEl); } else { head.appendChild(styleNode); } } if (oldStyleNode && keepOldStyleNode === false) { oldStyleNode.parentNode.removeChild(oldStyleNode); } // For IE. // This needs to happen *after* the style element is added to the DOM, otherwise IE 7 and 8 may crash. // See http://social.msdn.microsoft.com/Forums/en-US/7e081b65-878a-4c22-8e68-c10d39c2ed32/internet-explorer-crashes-appending-style-element-to-head if (styleNode.styleSheet) { try { styleNode.styleSheet.cssText = styles; } catch (e) { throw new Error('Couldn\'t reassign styleSheet.cssText.'); } } }, currentScript: function(window) { const document = window.document; return document.currentScript || (() => { const scripts = document.getElementsByTagName('script'); return scripts[scripts.length - 1]; })(); } }; /** * @param {Window} window * @param {Record<string, *>} options */ var addDefaultOptions = (window, options) => { // use options from the current script tag data attribues addDataAttr(options, browser$1.currentScript(window)); if (options.isFileProtocol === undefined) { options.isFileProtocol = /^(file|(chrome|safari)(-extension)?|resource|qrc|app):/.test(window.location.protocol); } // Load styles asynchronously (default: false) // // This is set to `false` by default, so that the body // doesn't start loading before the stylesheets are parsed. // Setting this to `true` can result in flickering. // options.async = options.async || false; options.fileAsync = options.fileAsync || false; // Interval between watch polls options.poll = options.poll || (options.isFileProtocol ? 1000 : 1500); options.env = options.env || (window.location.hostname == '127.0.0.1' || window.location.hostname == '0.0.0.0' || window.location.hostname == 'localhost' || (window.location.port && window.location.port.length > 0) || options.isFileProtocol ? 'development' : 'production'); const dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(window.location.hash); if (dumpLineNumbers) { options.dumpLineNumbers = dumpLineNumbers[1]; } if (options.useFileCache === undefined) { options.useFileCache = true; } if (options.onReady === undefined) { options.onReady = true; } if (options.relativeUrls) { options.rewriteUrls = 'all'; } }; var logger$1 = { error: function(msg) { this._fireEvent('error', msg); }, warn: function(msg) { this._fireEvent('warn', msg); }, info: function(msg) { this._fireEvent('info', msg); }, debug: function(msg) { this._fireEvent('debug', msg); }, addListener: function(listener) { this._listeners.push(listener); }, removeListener: function(listener) { for (let i = 0; i < this._listeners.length; i++) { if (this._listeners[i] === listener) { this._listeners.splice(i, 1); return; } } }, _fireEvent: function(type, msg) { for (let i = 0; i < this._listeners.length; i++) { const logFunction = this._listeners[i][type]; if (logFunction) { logFunction(msg); } } }, _listeners: [] }; /** * @todo Document why this abstraction exists, and the relationship between * environment, file managers, and plugin manager */ class Environment { constructor(externalEnvironment, fileManagers) { this.fileManagers = fileManagers || []; externalEnvironment = externalEnvironment || {}; const optionalFunctions = ['encodeBase64', 'mimeLookup', 'charsetLookup', 'getSourceMapGenerator']; const requiredFunctions = []; const functions = requiredFunctions.concat(optionalFunctions); for (let i = 0; i < functions.length; i++) { const propName = functions[i]; const environmentFunc = externalEnvironment[propName]; if (environmentFunc) { this[propName] = environmentFunc.bind(externalEnvironment); } else if (i < requiredFunctions.length) { this.warn(`missing required function in environment - ${propName}`); } } } getFileManager(filename, currentDirectory, options, environment, isSync) { if (!filename) { logger$1.warn('getFileManager called with no filename.. Please report this issue. continuing.'); } if (currentDirectory === undefined) { logger$1.warn('getFileManager called with null directory.. Please report this issue. continuing.'); } let fileManagers = this.fileManagers; if (options.pluginManager) { fileManagers = [].concat(fileManagers).concat(options.pluginManager.getFileManagers()); } for (let i = fileManagers.length - 1; i >= 0 ; i--) { const fileManager = fileManagers[i]; if (fileManager[isSync ? 'supportsSync' : 'supports'](filename, currentDirectory, options, environment)) { return fileManager; } } return null; } addFileManager(fileManager) { this.fileManagers.push(fileManager); } clearFileManagers() { this.fileManagers = []; } } var colors = { 'aliceblue':'#f0f8ff', 'antiquewhite':'#faebd7', 'aqua':'#00ffff', 'aquamarine':'#7fffd4', 'azure':'#f0ffff', 'beige':'#f5f5dc', 'bisque':'#ffe4c4', 'black':'#000000', 'blanchedalmond':'#ffebcd', 'blue':'#0000ff', 'blueviolet':'#8a2be2', 'brown':'#a52a2a', 'burlywood':'#deb887', 'cadetblue':'#5f9ea0', 'chartreuse':'#7fff00', 'chocolate':'#d2691e', 'coral':'#ff7f50', 'cornflowerblue':'#6495ed', 'cornsilk':'#fff8dc', 'crimson':'#dc143c', 'cyan':'#00ffff', 'darkblue':'#00008b', 'darkcyan':'#008b8b', 'darkgoldenrod':'#b8860b', 'darkgray':'#a9a9a9', 'darkgrey':'#a9a9a9', 'darkgreen':'#006400', 'darkkhaki':'#bdb76b', 'darkmagenta':'#8b008b', 'darkolivegreen':'#556b2f', 'darkorange':'#ff8c00', 'darkorchid':'#9932cc', 'darkred':'#8b0000', 'darksalmon':'#e9967a', 'darkseagreen':'#8fbc8f', 'darkslateblue':'#483d8b', 'darkslategray':'#2f4f4f', 'darkslategrey':'#2f4f4f', 'darkturquoise':'#00ced1', 'darkviolet':'#9400d3', 'deeppink':'#ff1493', 'deepskyblue':'#00bfff', 'dimgray':'#696969', 'dimgrey':'#696969', 'dodgerblue':'#1e90ff', 'firebrick':'#b22222', 'floralwhite':'#fffaf0', 'forestgreen':'#228b22', 'fuchsia':'#ff00ff', 'gainsboro':'#dcdcdc', 'ghostwhite':'#f8f8ff', 'gold':'#ffd700', 'goldenrod':'#daa520', 'gray':'#808080', 'grey':'#808080', 'green':'#008000', 'greenyellow':'#adff2f', 'honeydew':'#f0fff0', 'hotpink':'#ff69b4', 'indianred':'#cd5c5c', 'indigo':'#4b0082', 'ivory':'#fffff0', 'khaki':'#f0e68c', 'lavender':'#e6e6fa', 'lavenderblush':'#fff0f5', 'lawngreen':'#7cfc00', 'lemonchiffon':'#fffacd', 'lightblue':'#add8e6', 'lightcoral':'#f08080', 'lightcyan':'#e0ffff', 'lightgoldenrodyellow':'#fafad2', 'lightgray':'#d3d3d3', 'lightgrey':'#d3d3d3', 'lightgreen':'#90ee90', 'lightpink':'#ffb6c1', 'lightsalmon':'#ffa07a', 'lightseagreen':'#20b2aa', 'lightskyblue':'#87cefa', 'lightslategray':'#778899', 'lightslategrey':'#778899', 'lightsteelblue':'#b0c4de', 'lightyellow':'#ffffe0', 'lime':'#00ff00', 'limegreen':'#32cd32', 'linen':'#faf0e6', 'magenta':'#ff00ff', 'maroon':'#800000', 'mediumaquamarine':'#66cdaa', 'mediumblue':'#0000cd', 'mediumorchid':'#ba55d3', 'mediumpurple':'#9370d8', 'mediumseagreen':'#3cb371', 'mediumslateblue':'#7b68ee', 'mediumspringgreen':'#00fa9a', 'mediumturquoise':'#48d1cc', 'mediumvioletred':'#c71585', 'midnightblue':'#191970', 'mintcream':'#f5fffa', 'mistyrose':'#ffe4e1', 'moccasin':'#ffe4b5', 'navajowhite':'#ffdead', 'navy':'#000080', 'oldlace':'#fdf5e6', 'olive':'#808000', 'olivedrab':'#6b8e23', 'orange':'#ffa500', 'orangered':'#ff4500', 'orchid':'#da70d6', 'palegoldenrod':'#eee8aa', 'palegreen':'#98fb98', 'paleturquoise':'#afeeee', 'palevioletred':'#d87093', 'papayawhip':'#ffefd5', 'peachpuff':'#ffdab9', 'peru':'#cd853f', 'pink':'#ffc0cb', 'plum':'#dda0dd', 'powderblue':'#b0e0e6', 'purple':'#800080', 'rebeccapurple':'#663399', 'red':'#ff0000', 'rosybrown':'#bc8f8f', 'royalblue':'#4169e1', 'saddlebrown':'#8b4513', 'salmon':'#fa8072', 'sandybrown':'#f4a460', 'seagreen':'#2e8b57', 'seashell':'#fff5ee', 'sienna':'#a0522d', 'silver':'#c0c0c0', 'skyblue':'#87ceeb', 'slateblue':'#6a5acd', 'slategray':'#708090', 'slategrey':'#708090', 'snow':'#fffafa', 'springgreen':'#00ff7f', 'steelblue':'#4682b4', 'tan':'#d2b48c', 'teal':'#008080', 'thistle':'#d8bfd8', 'tomato':'#ff6347', 'turquoise':'#40e0d0', 'violet':'#ee82ee', 'wheat':'#f5deb3', 'white':'#ffffff', 'whitesmoke':'#f5f5f5', 'yellow':'#ffff00', 'yellowgreen':'#9acd32' }; var unitConversions = { length: { 'm': 1, 'cm': 0.01, 'mm': 0.001, 'in': 0.0254, 'px': 0.0254 / 96, 'pt': 0.0254 / 72, 'pc': 0.0254 / 72 * 12 }, duration: { 's': 1, 'ms': 0.001 }, angle: { 'rad': 1 / (2 * Math.PI), 'deg': 1 / 360, 'grad': 1 / 400, 'turn': 1 } }; var data = { colors, unitConversions }; // @ts-check /** * @typedef {object} FileInfo * @property {string} [filename] * @property {string} [rootpath] * @property {string} [currentDirectory] * @property {string} [rootFilename] * @property {string} [entryPath] * @property {boolean} [reference] */ /** * @typedef {object} VisibilityInfo * @property {number} [visibilityBlocks] * @property {boolean} [nodeVisible] */ /** * @typedef {object} CSSOutput * @property {(chunk: string, fileInfo?: FileInfo, index?: number, mapLines?: boolean) => void} add * @property {() => boolean} isEmpty */ /** * @typedef {object} EvalContext * @property {number} [numPrecision] * @property {(op?: string) => boolean} [isMathOn] * @property {number} [math] * @property {Node[]} [frames] * @property {Array<{important?: string}>} [importantScope] * @property {string[]} [paths] * @property {boolean} [compress] * @property {boolean} [strictUnits] * @property {boolean} [sourceMap] * @property {boolean} [importMultiple] * @property {string} [urlArgs] * @property {boolean} [javascriptEnabled] * @property {object} [pluginManager] * @property {number} [rewriteUrls] * @property {boolean} [inCalc] * @property {boolean} [mathOn] * @property {boolean[]} [calcStack] * @property {boolean[]} [parensStack] * @property {Node[]} [mediaBlocks] * @property {Node[]} [mediaPath] * @property {() => void} [inParenthesis] * @property {() => void} [outOfParenthesis] * @property {() => void} [enterCalc] * @property {() => void} [exitCalc] * @property {(path: string) => boolean} [pathRequiresRewrite] * @property {(path: string, rootpath?: string) => string} [rewritePath] * @property {(path: string) => string} [normalizePath] * @property {number} [tabLevel] * @property {boolean} [lastRule] */ /** * @typedef {object} TreeVisitor * @property {(node: Node) => Node} visit * @property {(nodes: Node[], nonReplacing?: boolean) => Node[]} visitArray */ /** * The reason why Node is a class and other nodes simply do not extend * from Node (since we're transpiling) is due to this issue: * * @see https://github.com/less/less.js/issues/3434 */ class Node { get type() { return ''; } constructor() { /** @type {Node | null} */ this.parent = null; /** @type {number | undefined} */ this.visibilityBlocks = undefined; /** @type {boolean | undefined} */ this.nodeVisible = undefined; /** @type {Node | null} */ this.rootNode = null; /** @type {Node | null} */ this.parsed = null; /** @type {Node | Node[] | string | number | undefined} */ this.value = undefined; /** @type {number | undefined} */ this._index = undefined; /** @type {FileInfo | undefined} */ this._fileInfo = undefined; } get currentFileInfo() { return this.fileInfo(); } get index() { return this.getIndex(); } /** * @param {Node | Node[]} nodes * @param {Node} parent */ setParent(nodes, parent) { /** @param {Node} node */ function set(node) { if (node && node instanceof Node) { node.parent = parent; } } if (Array.isArray(nodes)) { nodes.forEach(set); } else { set(nodes); } } /** @returns {number} */ getIndex() { return this._index || (this.parent && this.parent.getIndex()) || 0; } /** @returns {FileInfo} */ fileInfo() { return this._fileInfo || (this.parent && this.parent.fileInfo()) || {}; } /** @returns {boolean} */ isRulesetLike() { return false; } /** * @param {EvalContext} context * @returns {string} */ toCSS(context) { /** @type {string[]} */ const strs = []; this.genCSS(context, { add: function(chunk, fileInfo, index) { strs.push(chunk); }, isEmpty: function () { return strs.length === 0; } }); return strs.join(''); } /** * @param {EvalContext} context * @param {CSSOutput} output */ genCSS(context, output) { output.add(/** @type {string} */ (this.value)); } /** * @param {TreeVisitor} visitor */ accept(visitor) { this.value = visitor.visit(/** @type {Node} */ (this.value)); } /** * @param {EvalContext} [context] * @returns {Node} */ eval(context) { return this; } /** * @param {EvalContext} context * @param {string} op * @param {number} a * @param {number} b * @returns {number | undefined} */ _operate(context, op, a, b) { switch (op) { case '+': return a + b; case '-': return a - b; case '*': return a * b; case '/': return a / b; } } /** * @param {EvalContext} context * @param {number} value * @returns {number} */ fround(context, value) { const precision = context && context.numPrecision; // add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999...) are properly rounded: return (precision) ? Number((value + 2e-16).toFixed(precision)) : value; } /** * @param {Node & { compare?: (other: Node) => number | undefined }} a * @param {Node & { compare?: (other: Node) => number | undefined }} b * @returns {number | undefined} */ static compare(a, b) { /* returns: -1: a < b 0: a = b 1: a > b and *any* other value for a != b (e.g. undefined, NaN, -2 etc.) */ if ((a.compare) && // for "symmetric results" force toCSS-based comparison // of Quoted or Anonymous if either value is one of those !(b.type === 'Quoted' || b.type === 'Anonymous')) { return a.compare(b); } else if (b.compare) { return -b.compare(a); } else if (a.type !== b.type) { return undefined; } let aVal = a.value; let bVal = b.value; if (!Array.isArray(aVal)) { return aVal === bVal ? 0 : undefined; } if (!Array.isArray(bVal)) { return undefined; } if (aVal.length !== bVal.length) { return undefined; } for (let i = 0; i < aVal.length; i++) { if (Node.compare(aVal[i], bVal[i]) !== 0) { return undefined; } } return 0; } /** * @param {number | string} a * @param {number | string} b * @returns {number | undefined} */ static numericCompare(a, b) { return a < b ? -1 : a === b ? 0 : a > b ? 1 : undefined; } /** @returns {boolean} */ blocksVisibility() { if (this.visibilityBlocks === undefined) { this.visibilityBlocks = 0; } return this.visibilityBlocks !== 0; } addVisibilityBlock() { if (this.visibilityBlocks === undefined) { this.visibilityBlocks = 0; } this.visibilityBlocks = this.visibilityBlocks + 1; } removeVisibilityBlock() { if (this.visibilityBlocks === undefined) { this.visibilityBlocks = 0; } this.visibilityBlocks = this.visibilityBlocks - 1; } ensureVisibility() { this.nodeVisible = true; } ensureInvisibility() { this.nodeVisible = false; } /** @returns {boolean | undefined} */ isVisible() { return this.nodeVisible; } /** @returns {VisibilityInfo} */ visibilityInfo() { return { visibilityBlocks: this.visibilityBlocks, nodeVisible: this.nodeVisible }; } /** @param {VisibilityInfo} info */ copyVisibilityInfo(info) { if (!info) { return; } this.visibilityBlocks = info.visibilityBlocks; this.nodeVisible = info.nodeVisible; } } /** * Set by the parser at runtime on Node.prototype. * @type {{ context: EvalContext, importManager: object, imports: object } | undefined} */ Node.prototype.parse = undefined; // @ts-check /** @import { EvalContext, CSSOutput } from './node.js' */ // // RGB Colors - #ff0014, #eee // class Color extends Node { get type() { return 'Color'; } /** * @param {number[] | string} rgb * @param {number} [a] * @param {string} [originalForm] */ constructor(rgb, a, originalForm) { super(); const self = this; // // The end goal here, is to parse the arguments // into an integer triplet, such as `128, 255, 0` // // This facilitates operations and conversions. // if (Array.isArray(rgb)) { /** @type {number[]} */ this.rgb = rgb; } else if (rgb.length >= 6) { /** @type {number[]} */ this.rgb = []; /** @type {RegExpMatchArray} */ (rgb.match(/.{2}/g)).map(function (c, i) { if (i < 3) { self.rgb.push(parseInt(c, 16)); } else { self.alpha = (parseInt(c, 16)) / 255; } }); } else { /** @type {number[]} */ this.rgb = []; rgb.split('').map(function (c, i) { if (i < 3) { self.rgb.push(parseInt(c + c, 16)); } else { self.alpha = (parseInt(c + c, 16)) / 255; } }); } /** @type {number} */ if (typeof this.alpha === 'undefined') { this.alpha = (typeof a === 'number') ? a : 1; } if (typeof originalForm !== 'undefined') { this.value = originalForm; } } luma() { let r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255; r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4); g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4); b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4); return 0.2126 * r + 0.7152 * g + 0.0722 * b; } /** * @param {EvalContext} context * @param {CSSOutput} output */ genCSS(context, output) { output.add(this.toCSS(context)); } /** * @param {EvalContext} context * @param {boolean} [doNotCompress] * @returns {string} */ toCSS(context, doNotCompress) { const compress = context && context.compress && !doNotCompress; let color; let alpha; /** @type {string | undefined} */ let colorFunction; /** @type {(string | number)[]} */ let args = []; // `value` is set if this color was originally // converted from a named color string so we need // to respect this and try to output named color too. alpha = this.fround(context, this.alpha); if (this.value) { if (/** @type {string} */ (this.value).indexOf('rgb') === 0) { if (alpha < 1) { colorFunction = 'rgba'; } } else if (/** @type {string} */ (this.value).indexOf('hsl') === 0) { if (alpha < 1) { colorFunction = 'hsla'; } else { colorFunction = 'hsl'; } } else { return /** @type {string} */ (this.value); } } else { if (alpha < 1) { colorFunction = 'rgba'; } } switch (colorFunction) { case 'rgba': args = this.rgb.map(function (c) { return clamp$1(Math.round(c), 255); }).concat(clamp$1(alpha, 1)); break; case 'hsla': args.push(clamp$1(alpha, 1)); // eslint-disable-next-line no-fallthrough case 'hsl': color = this.toHSL(); args = [ this.fround(context, color.h), `${this.fround(context, color.s * 100)}%`, `${this.fround(context, color.l * 100)}%` ].concat(args); } if (colorFunction) { // Values are capped between `0` and `255`, rounded and zero-padded. return `${colorFunction}(${args.join(`,${compress ? '' : ' '}`)})`; } color = this.toRGB(); if (compress) { const splitcolor = color.split(''); // Convert color to short format if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) { color = `#${splitcolor[1]}${splitcolor[3]}${splitcolor[5]}`; } } return color; } // // Operations have to be done per-channel, if not, // channels will spill onto each other. Once we have // our result, in the form of an integer triplet, // we create a new Color node to hold the result. // /** * @param {EvalContext} context * @param {string} op * @param {Color} other */ operate(context, op, other) { const rgb = new Array(3); const alpha = this.alpha * (1 - other.alpha) + other.alpha; for (let c = 0; c < 3; c++) { rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]); } return new Color(rgb, alpha); } toRGB() { return toHex(this.rgb); } toHSL() { const r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255, a = this.alpha; const max = Math.max(r, g, b), min = Math.min(r, g, b); /** @type {number} */ let h; let s; const l = (max + min) / 2; const d = max - min; if (max === min) { h = s = 0; } else { s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } /** @type {number} */ (h) /= 6; } return { h: /** @type {number} */ (h) * 360, s, l, a }; } // Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript toHSV() { const r = this.rgb[0] / 255, g = this.rgb[1] / 255, b = this.rgb[2] / 255, a = this.alpha; const max = Math.max(r, g, b), min = Math.min(r, g, b); /** @type {number} */ let h; let s; const v = max; const d = max - min; if (max === 0) { s = 0; } else { s = d / max; } if (max === min) { h = 0; } else { switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } /** @type {number} */ (h) /= 6; } return { h: /** @type {number} */ (h) * 360, s, v, a }; } toARGB() { return toHex([this.alpha * 255].concat(this.rgb)); } /** * @param {Node & { rgb?: number[], alpha?: number }} x * @returns {0 | undefined} */ compare(x) { return (x.rgb && x.rgb[0] === this.rgb[0] && x.rgb[1] === this.rgb[1] && x.rgb[2] === this.rgb[2] && x.alpha === this.alpha) ? 0 : undefined; } /** @param {string} keyword */ static fromKeyword(keyword) { /** @type {Color | undefined} */ let c; const key = keyword.toLowerCase(); // eslint-disable-next-line no-prototype-builtins if (colors.hasOwnProperty(key)) { c = new Color(/** @type {string} */ (colors[/** @type {keyof typeof colors} */ (key)]).slice(1)); } else if (key === 'transparent') { c = new Color([0, 0, 0], 0); } if (c) { c.value = keyword; return c; } } } /** * @param {number} v * @param {number} max */ function clamp$1(v, max) { return Math.min(Math.max(v, 0), max); } /** @param {number[]} v */ function toHex(v) { return `#${v.map(function (c) { c = clamp$1(Math.round(c), 255); return (c < 16 ? '0' : '') + c.toString(16); }).join('')}`; } // @ts-check class Paren extends Node { get type() { return 'Paren'; } /** @param {Node} node */ constructor(node) { super(); this.value = node; /** @type {boolean | undefined} */ this.noSpacing = undefined; } /** * @param {EvalContext} context * @param {CSSOutput} output */ genCSS(context, output) { output.add('('); /** @type {Node} */ (this.value).genCSS(context, output); output.add(')'); } /** * @param {EvalContext} context * @returns {Paren} */ eval(context) { const paren = new Paren(/** @type {Node} */ (this.value).eval(context)); if (this.noSpacing) { paren.noSpacing = true; } return paren; } } // @ts-check /** @import { EvalContext, CSSOutput } from './node.js' */ /** @type {Record<string, boolean>} */ const _noSpaceCombinators = { '': true, ' ': true, '|': true }; class Combinator extends Node { get type() { return 'Combinator'; } /** @param {string} value */ constructor(value) { super(); if (value === ' ') { this.value = ' '; this.emptyOrWhitespace = true; } else { this.value = value ? value.trim() : ''; this.emptyOrWhitespace = this.value === ''; } } /** * @param {EvalContext} context * @param {CSSOutput} output */ genCSS(context, output) { const spaceOrEmpty = (context.compress || _noSpaceCombinators[/** @type {string} */ (this.value)]) ? '' : ' '; output.add(spaceOrEmpty + this.value + spaceOrEmpty); } } // @ts-check class Element extends Node { get type() { return 'Element'; } /** * @param {Combinator | string} combinator * @param {string | Node} value * @param {boolean} [isVariable] * @param {number} [index] * @param {FileInfo} [currentFileInfo] * @param {VisibilityInfo} [visibilityInfo] */ constructor(combinator, value, isVariable, index, currentFileInfo, visibilityInfo) { super(); this.combinator = combinator instanceof Combinator ? combinator : new Combinator(combinator); if (typeof value === 'string') { this.value = value.trim(); } else if (value) { this.value = value; } else { this.value = ''; } /** @type {boolean | undefined} */ this.isVariable = isVariable; this._index = index; this._fileInfo = currentFileInfo; this.copyVisibilityInfo(visibilityInfo); this.setParent(this.combinator, this); } /** @param {TreeVisitor} visitor */ accept(visitor) { const value = this.value; this.combinator = /** @type {Combinator} */ (visitor.visit(this.combinator)); if (typeof value === 'object') { this.value = visitor.visit(/** @type {Node} */ (value)); } } /** @param {EvalContext} context */ eval(context) { return new Element(this.combinator, /** @type {Node} */ (this.value).eval ? /** @type {Node} */ (this.value).eval(context) : /** @type {string} */ (this.value), this.isVariable, this.getIndex(), this.fileInfo(), this.visibilityInfo()); } clone() { return new Element(this.combinator, /** @type {string | Node} */ (this.value), this.isVariable, this.getIndex(), this.fileInfo(), this.visibilityInfo()); } /** * @param {EvalContext} context * @param {CSSOutput} output */ genCSS(context, output) { output.add(this.toCSS(context), this.fileInfo(), this.getIndex()); } /** @param {EvalContext} [context] */ toCSS(context) { /** @type {EvalContext & { firstSelector?: boolean }} */ const ctx = context || {}; let value = this.value; const firstSelector = ctx.firstSelector; if (value instanceof Paren) { // selector in parens should not be affected by outer selector // flags (breaks only interpolated selectors - see #1973) ctx.firstSelector = true; } value = /** @type {Node} */ (value).toCSS ? /** @type {Node} */ (value).toCSS(ctx) : /** @type {string} */ (value); ctx.firstSelector = firstSelector; if (value === '' && this.combinator.value.charAt(0) === '&') { return ''; } else { return this.combinator.toCSS(ctx) + value; } } } const Math$1 = { ALWAYS: 0, PARENS_DIVISION: 1, PARENS: 2 // removed - STRICT_LEGACY: 3 }; const RewriteUrls = { OFF: 0, LOCAL: 1, ALL: 2 }; function getType(payload) { return Object.prototype.toString.call(payload).slice(8, -1); } function isArray(payload) { return getType(payload) === "Array"; } function isPlainObject(payload) { if (getType(payload) !== "Object") return false; const prototype = Object.getPrototypeOf(payload); return !!prototype && prototype.constructor === Object && prototype === Object.prototype; } function assignProp(carry, key, newVal, originalObject, includeNonenumerable) { const propType = {}.propertyIsEnumerable.call(originalObject, key) ? "enumerable" : "nonenumerable"; if (propType === "enumerable") carry[key] = newVal; if (includeNonenumerable && propType === "nonenumerable") { Object.defineProperty(carry, key, { value: newVal, enumerable: false, writable: true, configurable: true }); } } function copy(target, options = {}) { if (isArray(target)) { return target.map((item) => copy(item, options)); } if (!isPlainObject(target)) { return target; } const props = Object.getOwnPropertyNames(target); const symbols = Object.getOwnPropertySymbols(target); return [...props, ...symbols].reduce((carry, key) => { if (isArray(options.props) && !options.props.includes(key)) { return carry; } const val = target[key]; const newVal = copy(val, options); assignProp(carry, key, newVal, target, options.nonenumerable); return carry; }, {}); } /* jshint proto: true */ function getLocation(index, inputStream) { let n = index + 1; let line = null; let column = -1; while (--n >= 0 && inputStream.charAt(n) !== '\n') { column++; } if (typeof index === 'number') { line = (inputStream.slice(0, index).match(/\n/g) || '').length; } return { line, column }; } function copyArray(arr) { let i; const length = arr.length; const copy = new Array(length); for (i = 0; i < length; i++) { copy[i] = arr[i]; } return copy; } function clone(obj) { const cloned = {}; for (const prop in obj) { if (Object.prototype.hasOwnProperty.call(obj, prop)) { cloned[prop] = obj[prop]; } } return cloned; } function defaults(obj1, obj2) { let newObj = obj2 || {}; if (!obj2._defaults) { newObj = {}; const defaults = copy(obj1); newObj._defaults = defaults; const cloned = obj2 ? copy(obj2) : {}; Object.assign(newObj, defaults, cloned); } return newObj; } function copyOptions(obj1, obj2) { if (obj2 && obj2._defaults) { return obj2; } const opts = defaults(obj1, obj2); if (opts.strictMath) { opts.math = Math$1.PARENS; } // Back compat with changed relativeUrls option if (opts.relativeUrls) { opts.rewriteUrls = RewriteUrls.ALL; } if (typeof opts.math === 'string') { switch (opts.math.toLowerCase()) { case 'always': opts.math = Math$1.ALWAYS; break; case 'parens-division': opts.math = Math$1.PARENS_DIVISION; break; case 'strict': case 'parens': opts.math = Math$1.PARENS; break; default: opts.math = Math$1.PARENS; } } if (typeof opts.rewriteUrls === 'string') { switch (opts.rewriteUrls.toLowerCase()) { case 'off': opts.rewriteUrls = RewriteUrls.OFF; break; case 'local': opts.rewriteUrls = RewriteUrls.LOCAL; break; case 'all': opts.rewriteUrls = RewriteUrls.ALL; break; } } return opts; } function merge(obj1, obj2) { for (const prop in obj2) { if (Object.prototype.hasOwnProperty.call(obj2, prop)) { obj1[prop] = obj2[prop]; } } return obj1; } function flattenArray(arr, result = []) { for (let i = 0, length = arr.length; i < length; i++) { const value = arr[i]; if (Array.isArray(value)) { flattenArray(value, result); } else { if (value !== undefined) { result.push(value); } } } return result; } function isNullOrUndefined(val) { return val === null || val === undefined } var utils = /*#__PURE__*/Object.freeze({ __proto__: null, getLocation: getLocation, copyArray: copyArray, clone: clone, defaults: defaults, copyOptions: copyOptions, merge: merge, flattenArray: flattenArray, isNullOrUndefined: isNullOrUndefined }); const anonymousFunc = /(<anonymous>|Function):(\d+):(\d+)/; /** * This is a centralized class of any error that could be thrown internally (mostly by the parser). * Besides standard .message it keeps some additional data like a path to the file where the error * occurred along with line and column numbers. * * @class * @extends Error * @type {module.LessError} * * @prop {string} type * @prop {string} filename * @prop {number} index * @prop {number} line * @prop {number} column * @prop {number} callLine * @prop {number} callExtract * @prop {string[]} extract * * @param {Object} e - An error object to wrap around or just a descriptive object * @param {Object} fileContentMap - An object with file contents in 'contents' property (like importManager) @todo - move to fileManager? * @param {string} [currentFilename] */ const LessError = function(e, fileContentMap, currentFilename) { Error.call(this); const filename = e.filename || currentFilename; this.message = e.message; this.stack = e.stack;