less
Version:
Leaner CSS
1,501 lines (1,337 loc) • 559 kB
JavaScript
/**
* 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;