diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa8b7eb7..e116e733 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,8 +18,8 @@ jobs: - lts/* - latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: ${{ matrix.node-version }} - run: npm ci diff --git a/.gitignore b/.gitignore index 8297d482..758d4490 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules npm-debug.log -lib/generated/implementedProperties.js lib/generated/properties.js +lib/generated/propertyList.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index a451cf9e..42beceee 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -4,13 +4,11 @@ */ "use strict"; -const allProperties = require("./generated/allProperties"); -const implementedProperties = require("./generated/implementedProperties"); -const generatedProperties = require("./generated/properties"); +const propertyList = require("./generated/propertyList"); const { borderProperties, getPositionValue, - normalizeBorderProperties, + normalizeProperties, prepareBorderProperties, prepareProperties, shorthandProperties @@ -22,113 +20,53 @@ const { parsePropertyValue, prepareValue } = require("./parsers"); -const allExtraProperties = require("./utils/allExtraProperties"); -const { dashedToCamelCase } = require("./utils/camelize"); -const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); +const ELEMENT_NODE = 1; + /** * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface */ class CSSStyleDeclaration { /** - * @param {Function} onChangeCallback - * @param {object} [opt] - * @param {object} [opt.context] - Window, Element or CSSRule. + * @param {object} globalObject - Window + * @param {object} opt - Options + * @param {object} opt.context - Element or CSSStyleRule + * @param {string} opt.format - "specifiedValue" or "computedValue" + * @param {Function} opt.onChange - Callback when cssText is changed or the property is removed */ - constructor(onChangeCallback, opt = {}) { - // Make constructor and internals non-enumerable. - Object.defineProperties(this, { - constructor: { - enumerable: false, - writable: true - }, - - // Window - _global: { - value: globalThis, - enumerable: false, - writable: true - }, - - // Element - _ownerNode: { - value: null, - enumerable: false, - writable: true - }, - - // CSSRule - _parentNode: { - value: null, - enumerable: false, - writable: true - }, - - _onChange: { - value: null, - enumerable: false, - writable: true - }, - - _values: { - value: new Map(), - enumerable: false, - writable: true - }, - - _priorities: { - value: new Map(), - enumerable: false, - writable: true - }, - - _length: { - value: 0, - enumerable: false, - writable: true - }, - - _computed: { - value: false, - enumerable: false, - writable: true - }, - - _readonly: { - value: false, - enumerable: false, - writable: true - }, - - _setInProgress: { - value: false, - enumerable: false, - writable: true - } - }); - - const { context } = opt; + constructor(globalObject, opt = {}) { + const { context, format, onChange } = opt; + // These help interface with jsdom. + this._global = globalObject; + this._onChange = onChange; + + // These correspond to https://drafts.csswg.org/cssom/#css-declaration-block. + this._computed = format === "computedValue"; + this._ownerNode = null; + this._parentRule = null; if (context) { - if (typeof context.getComputedStyle === "function") { - this._global = context; - this._computed = true; - this._readonly = true; - } else if (context.nodeType === 1 && Object.hasOwn(context, "style")) { - this._global = context.ownerDocument.defaultView; + // The context is an element. + if (context.nodeType === ELEMENT_NODE) { this._ownerNode = context; + // The context is a CSSStyleRule. } else if (Object.hasOwn(context, "parentRule")) { this._parentRule = context; - // Find Window from the owner node of the StyleSheet. - const window = context?.parentStyleSheet?.ownerNode?.ownerDocument?.defaultView; - if (window) { - this._global = window; - } } } - if (typeof onChangeCallback === "function") { - this._onChange = onChangeCallback; - } + this._readonly = false; + this._updating = false; + + // These correspond to the specification's "declarations". + this._values = new Map(); + this._priorities = new Map(); + this._length = 0; + + // This is used internally by parsers. e.g. parsers.resolveCalc(), parsers.parseColor(), etc. + // Note that options may be updated later to resolve getComputedStyle(). See setOptions() below. + this._options = { + format: format === "computedValue" ? format : "specifiedValue" + }; } get cssText() { @@ -150,7 +88,7 @@ class CSSStyleDeclaration { } properties.set(property, { property, value, priority }); } - const normalizedProperties = normalizeBorderProperties(properties); + const normalizedProperties = normalizeProperties(properties); const parts = []; for (const { property, value, priority } of normalizedProperties.values()) { if (priority) { @@ -171,34 +109,26 @@ class CSSStyleDeclaration { Array.prototype.splice.call(this, 0, this._length); this._values.clear(); this._priorities.clear(); - if (this._parentRule || (this._ownerNode && this._setInProgress)) { + if (this._parentRule || (this._ownerNode && this._updating)) { return; } - this._setInProgress = true; + this._updating = true; try { + // TBD: use cache? const valueObj = parseCSS( val, { - context: "declarationList", - parseValue: false + globalObject: this._global, + options: { + context: "declarationList", + parseValue: false + } }, true ); if (valueObj?.children) { const properties = new Map(); - let shouldSkipNext = false; for (const item of valueObj.children) { - if (item.type === "Atrule") { - continue; - } - if (item.type === "Rule") { - shouldSkipNext = true; - continue; - } - if (shouldSkipNext === true) { - shouldSkipNext = false; - continue; - } const { important, property, @@ -217,8 +147,10 @@ class CSSStyleDeclaration { properties.set(property, { property, value, priority }); } } else { + // TBD: use cache? const parsedValue = parsePropertyValue(property, value, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (parsedValue) { if (properties.has(property)) { @@ -236,7 +168,8 @@ class CSSStyleDeclaration { } } const parsedProperties = prepareProperties(properties, { - globalObject: this._global + globalObject: this._global, + options: this._options }); for (const [property, item] of parsedProperties) { const { priority, value } = item; @@ -247,7 +180,7 @@ class CSSStyleDeclaration { } catch { return; } - this._setInProgress = false; + this._updating = undefined; if (typeof this._onChange === "function") { this._onChange(this.cssText); } @@ -258,8 +191,7 @@ class CSSStyleDeclaration { } // This deletes indices if the new length is less then the current length. - // If the new length is more, it does nothing, the new indices will be - // undefined until set. + // If the new length is more, it does nothing, the new indices will be undefined until set. set length(len) { for (let i = len; i < this._length; i++) { delete this[i]; @@ -267,36 +199,6 @@ class CSSStyleDeclaration { this._length = len; } - // Readonly - get parentRule() { - return this._parentRule; - } - - get cssFloat() { - return this.getPropertyValue("float"); - } - - set cssFloat(value) { - this._setProperty("float", value); - } - - /** - * @param {string} property - */ - getPropertyPriority(property) { - return this._priorities.get(property) || ""; - } - - /** - * @param {string} property - */ - getPropertyValue(property) { - if (this._values.has(property)) { - return this._values.get(property).toString(); - } - return ""; - } - /** * @param {...number} args */ @@ -316,26 +218,18 @@ class CSSStyleDeclaration { /** * @param {string} property */ - removeProperty(property) { - if (this._readonly) { - const msg = `Property ${property} can not be modified.`; - const name = "NoModificationAllowedError"; - throw new this._global.DOMException(msg, name); - } - if (!this._values.has(property)) { - return ""; - } - const prevValue = this._values.get(property); - this._values.delete(property); - this._priorities.delete(property); - const index = Array.prototype.indexOf.call(this, property); - if (index >= 0) { - Array.prototype.splice.call(this, index, 1); - if (typeof this._onChange === "function") { - this._onChange(this.cssText); - } + getPropertyValue(property) { + if (this._values.has(property)) { + return this._values.get(property).toString(); } - return prevValue; + return ""; + } + + /** + * @param {string} property + */ + getPropertyPriority(property) { + return this._priorities.get(property) || ""; } /** @@ -362,7 +256,7 @@ class CSSStyleDeclaration { return; } const property = asciiLowercase(prop); - if (!allProperties.has(property) && !allExtraProperties.has(property)) { + if (!propertyList.has(property)) { return; } if (priority) { @@ -372,277 +266,297 @@ class CSSStyleDeclaration { } this[property] = value; } -} -// Internal methods -Object.defineProperties(CSSStyleDeclaration.prototype, { - _setProperty: { - /** - * @param {string} property - * @param {string} val - * @param {string} priority - */ - value(property, val, priority) { - if (typeof val !== "string") { - return; - } - if (val === "") { - this.removeProperty(property); - return; - } - let originalText = ""; + /** + * @param {string} property + */ + removeProperty(property) { + if (this._readonly) { + const msg = `Property ${property} can not be modified.`; + const name = "NoModificationAllowedError"; + throw new this._global.DOMException(msg, name); + } + if (!this._values.has(property)) { + return ""; + } + const prevValue = this._values.get(property); + this._values.delete(property); + this._priorities.delete(property); + const index = Array.prototype.indexOf.call(this, property); + if (index >= 0) { + Array.prototype.splice.call(this, index, 1); if (typeof this._onChange === "function") { - originalText = this.cssText; - } - if (this._values.has(property)) { - const index = Array.prototype.indexOf.call(this, property); - // The property already exists but is not indexed into `this` so add it. - if (index < 0) { - this[this._length] = property; - this._length++; - } - } else { - // New property. - this[this._length] = property; - this._length++; - } - if (priority === "important") { - this._priorities.set(property, priority); - } else { - this._priorities.delete(property); - } - this._values.set(property, val); - if ( - typeof this._onChange === "function" && - this.cssText !== originalText && - !this._setInProgress - ) { this._onChange(this.cssText); } - }, - enumerable: false - }, + } + return prevValue; + } - _borderSetter: { - /** - * @param {string} prop - * @param {object|Array|string} val - * @param {string} prior - */ - value(prop, val, prior) { - const properties = new Map(); - if (prop === "border") { - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - properties.set(prop, { propery: prop, value: val, priority }); - } else { - for (let i = 0; i < this._length; i++) { - const property = this[i]; - if (borderProperties.has(property)) { - const value = this.getPropertyValue(property); - const longhandPriority = this._priorities.get(property) ?? ""; - let priority = longhandPriority; - if (prop === property && typeof prior === "string") { - priority = prior; - } - properties.set(property, { property, value, priority }); - } - } - } - const parsedProperties = prepareBorderProperties(prop, val, prior, properties, { - globalObject: this._global - }); - for (const [property, item] of parsedProperties) { - const { priority, value } = item; - this._setProperty(property, value, priority); - } - }, - enumerable: false - }, + get parentRule() { + return this._parentRule; + } - _flexBoxSetter: { - /** - * @param {string} prop - * @param {string} val - * @param {string} prior - * @param {string} shorthandProperty - */ - value(prop, val, prior, shorthandProperty) { - if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { - return; - } - const shorthandPriority = this._priorities.get(shorthandProperty); - this.removeProperty(shorthandProperty); - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - this.removeProperty(prop); - if (shorthandPriority && priority) { - this._setProperty(prop, val); - } else { - this._setProperty(prop, val, priority); - } - if (val && !hasVarFunc(val)) { - const longhandValues = []; - const shorthandItem = shorthandProperties.get(shorthandProperty); - let hasGlobalKeyword = false; - for (const [longhandProperty] of shorthandItem.shorthandFor) { - if (longhandProperty === prop) { - if (isGlobalKeyword(val)) { - hasGlobalKeyword = true; - } - longhandValues.push(val); - } else { - const longhandValue = this.getPropertyValue(longhandProperty); - const longhandPriority = this._priorities.get(longhandProperty) ?? ""; - if (!longhandValue || longhandPriority !== priority) { - break; - } - if (isGlobalKeyword(longhandValue)) { - hasGlobalKeyword = true; - } - longhandValues.push(longhandValue); + /** + * Non-standard. + * To resolve getComputedStyle(), we need to setup additional option values. + * @param {object} opt - Options + * @returns {void} + */ + setOptions(opt = {}) { + for (const [key, value] of Object.entries(opt)) { + switch (key) { + case "format": { + if (value === "computedValue") { + this._computed = true; + this._options[key] = value; } + break; } - if (longhandValues.length === shorthandItem.shorthandFor.size) { - if (hasGlobalKeyword) { - const [firstValue, ...restValues] = longhandValues; - if (restValues.every((value) => value === firstValue)) { - this._setProperty(shorthandProperty, firstValue, priority); - } - } else { - const parsedValue = shorthandItem.parse(longhandValues.join(" ")); - const shorthandValue = Object.values(parsedValue).join(" "); - this._setProperty(shorthandProperty, shorthandValue, priority); - } + case "readOnly": { + this._readonly = value === true; + break; + } + default: { + this._options[key] = value; } } - }, - enumerable: false - }, + } + } - _positionShorthandSetter: { - /** - * @param {string} prop - * @param {Array|string} val - * @param {string} prior - */ - value(prop, val, prior) { - if (!shorthandProperties.has(prop)) { - return; - } - const shorthandValues = []; - if (Array.isArray(val)) { - shorthandValues.push(...val); - } else if (typeof val === "string") { - shorthandValues.push(val); - } else { - return; + // Internal methods + /** + * @param {string} property + * @param {string} val + * @param {string} priority + */ + _setProperty(property, val, priority) { + if (typeof val !== "string") { + return; + } + if (val === "") { + this.removeProperty(property); + return; + } + let originalText = ""; + if (typeof this._onChange === "function") { + originalText = this.cssText; + } + if (this._values.has(property)) { + const index = Array.prototype.indexOf.call(this, property); + // The property already exists but is not indexed into `this` so add it. + if (index < 0) { + this[this._length] = property; + this._length++; } + } else { + // New property. + this[this._length] = property; + this._length++; + } + if (priority === "important") { + this._priorities.set(property, priority); + } else { + this._priorities.delete(property); + } + this._values.set(property, val); + if (typeof this._onChange === "function" && this.cssText !== originalText && !this._updating) { + this._onChange(this.cssText); + } + } + + /** + * @param {string} prop + * @param {object|Array|string} val + * @param {string} prior + */ + _borderSetter(prop, val, prior) { + const properties = new Map(); + if (prop === "border") { let priority = ""; if (typeof prior === "string") { priority = prior; } else { priority = this._priorities.get(prop) ?? ""; } - const { position, shorthandFor } = shorthandProperties.get(prop); - let hasPriority = false; - for (const [longhandProperty, longhandItem] of shorthandFor) { - const { position: longhandPosition } = longhandItem; - const longhandValue = getPositionValue(shorthandValues, longhandPosition); - if (priority) { - this._setProperty(longhandProperty, longhandValue, priority); - } else { - const longhandPriority = this._priorities.get(longhandProperty) ?? ""; - if (longhandPriority) { - hasPriority = true; - } else { - this._setProperty(longhandProperty, longhandValue, priority); + properties.set(prop, { propery: prop, value: val, priority }); + } else { + for (let i = 0; i < this._length; i++) { + const property = this[i]; + if (borderProperties.has(property)) { + const value = this.getPropertyValue(property); + const longhandPriority = this._priorities.get(property) ?? ""; + let priority = longhandPriority; + if (prop === property && typeof prior === "string") { + priority = prior; } + properties.set(property, { property, value, priority }); } } - if (hasPriority) { - this.removeProperty(prop); - } else { - const shorthandValue = getPositionValue(shorthandValues, position); - this._setProperty(prop, shorthandValue, priority); - } - }, - enumerable: false - }, + } + const parsedProperties = prepareBorderProperties(prop, val, prior, properties, { + globalObject: this._global, + options: this._options + }); + for (const [property, item] of parsedProperties) { + const { priority, value } = item; + this._setProperty(property, value, priority); + } + } - _positionLonghandSetter: { - /** - * @param {string} prop - * @param {string} val - * @param {string} prior - * @param {string} shorthandProperty - */ - value(prop, val, prior, shorthandProperty) { - if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { - return; - } - const shorthandPriority = this._priorities.get(shorthandProperty); - this.removeProperty(shorthandProperty); - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - this.removeProperty(prop); - if (shorthandPriority && priority) { - this._setProperty(prop, val); - } else { - this._setProperty(prop, val, priority); - } - if (val && !hasVarFunc(val)) { - const longhandValues = []; - const { shorthandFor, position: shorthandPosition } = - shorthandProperties.get(shorthandProperty); - for (const [longhandProperty] of shorthandFor) { + /** + * @param {string} prop + * @param {string} val + * @param {string} prior + * @param {string} shorthandProperty + */ + _flexBoxSetter(prop, val, prior, shorthandProperty) { + if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { + return; + } + const shorthandPriority = this._priorities.get(shorthandProperty); + this.removeProperty(shorthandProperty); + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = this._priorities.get(prop) ?? ""; + } + this.removeProperty(prop); + if (shorthandPriority && priority) { + this._setProperty(prop, val); + } else { + this._setProperty(prop, val, priority); + } + if (val && !hasVarFunc(val)) { + const longhandValues = []; + const shorthandItem = shorthandProperties.get(shorthandProperty); + let hasGlobalKeyword = false; + for (const [longhandProperty] of shorthandItem.shorthandFor) { + if (longhandProperty === prop) { + if (isGlobalKeyword(val)) { + hasGlobalKeyword = true; + } + longhandValues.push(val); + } else { const longhandValue = this.getPropertyValue(longhandProperty); const longhandPriority = this._priorities.get(longhandProperty) ?? ""; if (!longhandValue || longhandPriority !== priority) { - return; + break; + } + if (isGlobalKeyword(longhandValue)) { + hasGlobalKeyword = true; } longhandValues.push(longhandValue); } - if (longhandValues.length === shorthandFor.size) { - const replacedValue = getPositionValue(longhandValues, shorthandPosition); - this._setProperty(shorthandProperty, replacedValue); + } + if (longhandValues.length === shorthandItem.shorthandFor.size) { + if (hasGlobalKeyword) { + const [firstValue, ...restValues] = longhandValues; + if (restValues.every((value) => value === firstValue)) { + this._setProperty(shorthandProperty, firstValue, priority); + } + } else { + const parsedValue = shorthandItem.parse(longhandValues.join(" ")); + if (parsedValue) { + const shorthandValue = Object.values(parsedValue).join(" "); + this._setProperty(shorthandProperty, shorthandValue, priority); + } } } - }, - enumerable: false + } } -}); -// Properties -Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties); + /** + * @param {string} prop + * @param {Array|string} val + * @param {string} prior + */ + _positionShorthandSetter(prop, val, prior) { + if (!shorthandProperties.has(prop)) { + return; + } + const shorthandValues = []; + if (Array.isArray(val)) { + shorthandValues.push(...val); + } else if (typeof val === "string") { + shorthandValues.push(val); + } else { + return; + } + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = this._priorities.get(prop) ?? ""; + } + const { position, shorthandFor } = shorthandProperties.get(prop); + let hasPriority = false; + for (const [longhandProperty, longhandItem] of shorthandFor) { + const { position: longhandPosition } = longhandItem; + const longhandValue = getPositionValue(shorthandValues, longhandPosition); + if (priority) { + this._setProperty(longhandProperty, longhandValue, priority); + } else { + const longhandPriority = this._priorities.get(longhandProperty) ?? ""; + if (longhandPriority) { + hasPriority = true; + } else { + this._setProperty(longhandProperty, longhandValue, priority); + } + } + } + if (hasPriority) { + this.removeProperty(prop); + } else { + const shorthandValue = getPositionValue(shorthandValues, position); + this._setProperty(prop, shorthandValue, priority); + } + } -// Additional properties -[...allProperties, ...allExtraProperties].forEach((property) => { - if (!implementedProperties.has(property)) { - const declaration = getPropertyDescriptor(property); - Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration); - const camel = dashedToCamelCase(property); - Object.defineProperty(CSSStyleDeclaration.prototype, camel, declaration); - if (/^webkit[A-Z]/.test(camel)) { - const pascal = camel.replace(/^webkit/, "Webkit"); - Object.defineProperty(CSSStyleDeclaration.prototype, pascal, declaration); + /** + * @param {string} prop + * @param {string} val + * @param {string} prior + * @param {string} shorthandProperty + */ + _positionLonghandSetter(prop, val, prior, shorthandProperty) { + if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { + return; + } + const shorthandPriority = this._priorities.get(shorthandProperty); + this.removeProperty(shorthandProperty); + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = this._priorities.get(prop) ?? ""; + } + this.removeProperty(prop); + if (shorthandPriority && priority) { + this._setProperty(prop, val); + } else { + this._setProperty(prop, val, priority); + } + if (val && !hasVarFunc(val)) { + const longhandValues = []; + const { shorthandFor, position: shorthandPosition } = + shorthandProperties.get(shorthandProperty); + for (const [longhandProperty] of shorthandFor) { + const longhandValue = this.getPropertyValue(longhandProperty); + const longhandPriority = this._priorities.get(longhandProperty) ?? ""; + if (!longhandValue || longhandPriority !== priority) { + return; + } + longhandValues.push(longhandValue); + } + if (longhandValues.length === shorthandFor.size) { + const replacedValue = getPositionValue(longhandValues, shorthandPosition); + this._setProperty(shorthandProperty, replacedValue, priority); + } } } -}); +} module.exports = { - CSSStyleDeclaration, - propertyList: Object.fromEntries(implementedProperties) + CSSStyleDeclaration }; diff --git a/lib/CSSStyleProperties.js b/lib/CSSStyleProperties.js new file mode 100644 index 00000000..d693ccfe --- /dev/null +++ b/lib/CSSStyleProperties.js @@ -0,0 +1,38 @@ +"use strict"; + +const { CSSStyleDeclaration } = require("./CSSStyleDeclaration"); +const generatedProperties = require("./generated/properties"); +const propertyList = require("./generated/propertyList"); +const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); + +/** + * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface + */ +class CSSStyleProperties extends CSSStyleDeclaration { + get cssFloat() { + return this.getPropertyValue("float"); + } + + set cssFloat(value) { + this.setProperty("float", value); + } +} + +// Properties +Object.defineProperties(CSSStyleProperties.prototype, generatedProperties); + +// Additional properties +for (const definition of propertyList.values()) { + const { legacyAliasOf, name, styleDeclaration } = definition; + const property = legacyAliasOf ?? name; + if (!Object.hasOwn(generatedProperties, property)) { + const declaration = getPropertyDescriptor(property); + for (const aliasProperty of styleDeclaration) { + Object.defineProperty(CSSStyleProperties.prototype, aliasProperty, declaration); + } + } +} + +module.exports = { + CSSStyleProperties +}; diff --git a/lib/generated/allProperties.js b/lib/generated/allProperties.js index a7a8c6ce..ac7b6aba 100644 --- a/lib/generated/allProperties.js +++ b/lib/generated/allProperties.js @@ -1,5 +1,5 @@ "use strict"; -// autogenerated - 2025-08-02 +// autogenerated - 2025-09-23 // https://www.w3.org/Style/CSS/all-properties.en.html module.exports = new Set([ @@ -310,7 +310,7 @@ module.exports = new Set([ "item-direction", "item-flow", "item-pack", - "item-slack", + "item-tolerance", "item-track", "item-wrap", "justify-content", diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 00000000..02b0527b --- /dev/null +++ b/lib/index.js @@ -0,0 +1,11 @@ +"use strict"; + +const { CSSStyleDeclaration } = require("./CSSStyleDeclaration"); +const { CSSStyleProperties } = require("./CSSStyleProperties"); +const propertyList = require("./generated/propertyList"); + +module.exports = { + CSSStyleDeclaration, + CSSStyleProperties, + propertyList: Object.fromEntries(propertyList) +}; diff --git a/lib/normalize.js b/lib/normalize.js index 6f7b1624..2b6110ee 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -1,6 +1,6 @@ "use strict"; -const implementedProperties = require("./generated/implementedProperties"); +const propertyList = require("./generated/propertyList"); const { hasVarFunc, isGlobalKeyword, isValidPropertyValue, splitValue } = require("./parsers"); const background = require("./properties/background"); const border = require("./properties/border"); @@ -16,38 +16,55 @@ const font = require("./properties/font"); const margin = require("./properties/margin"); const padding = require("./properties/padding"); -const borderImageProperty = "border-image"; +const BACKGROUND = "background"; +const BORDER = "border"; +const BORDER_BOTTOM = "border-bottom"; +const BORDER_COLOR = "border-color"; +const BORDER_IMAGE = "border-image"; +const BORDER_LEFT = "border-left"; +const BORDER_RIGHT = "border-right"; +const BORDER_STYLE = "border-style"; +const BORDER_TOP = "border-top"; +const BORDER_WIDTH = "border-width"; +const TOP = "top"; +const RIGHT = "right"; +const BOTTOM = "bottom"; +const LEFT = "left"; +const WIDTH = "width"; +const STYLE = "style"; +const COLOR = "color"; +const NONE = "none"; -exports.shorthandProperties = new Map([ - ["background", background], +const shorthandProperties = new Map([ + [BACKGROUND, background], [ - "border", + BORDER, { definition: border.definition, parse: border.parse, shorthandFor: new Map([ ...border.shorthandFor, ...border.positionShorthandFor, - [borderImageProperty, null] + [BORDER_IMAGE, null] ]) } ], - ["border-width", borderWidth], - ["border-style", borderStyle], - ["border-color", borderColor], - ["border-top", borderTop], - ["border-right", borderRight], - ["border-bottom", borderBottom], - ["border-left", borderLeft], + [BORDER_WIDTH, borderWidth], + [BORDER_STYLE, borderStyle], + [BORDER_COLOR, borderColor], + [BORDER_TOP, borderTop], + [BORDER_RIGHT, borderRight], + [BORDER_BOTTOM, borderBottom], + [BORDER_LEFT, borderLeft], ["flex", flex], ["font", font], ["margin", margin], ["padding", padding] ]); -exports.borderProperties = new Set([ - "border", - borderImageProperty, +const borderProperties = new Set([ + BORDER, + BORDER_IMAGE, ...border.shorthandFor.keys(), ...border.positionShorthandFor.keys(), ...borderTop.shorthandFor.keys(), @@ -56,7 +73,7 @@ exports.borderProperties = new Set([ ...borderLeft.shorthandFor.keys() ]); -exports.getPositionValue = (positionValues, position) => { +const getPositionValue = (positionValues, position) => { switch (positionValues.length) { case 1: { const [val1] = positionValues; @@ -65,16 +82,16 @@ exports.getPositionValue = (positionValues, position) => { case 2: { const [val1, val2] = positionValues; switch (position) { - case "top": { + case TOP: { return val1; } - case "right": { + case RIGHT: { return val2; } - case "bottom": { + case BOTTOM: { return val1; } - case "left": { + case LEFT: { return val2; } default: { @@ -88,16 +105,16 @@ exports.getPositionValue = (positionValues, position) => { case 3: { const [val1, val2, val3] = positionValues; switch (position) { - case "top": { + case TOP: { return val1; } - case "right": { + case RIGHT: { return val2; } - case "bottom": { + case BOTTOM: { return val3; } - case "left": { + case LEFT: { return val2; } default: { @@ -114,16 +131,16 @@ exports.getPositionValue = (positionValues, position) => { case 4: { const [val1, val2, val3, val4] = positionValues; switch (position) { - case "top": { + case TOP: { return val1; } - case "right": { + case RIGHT: { return val2; } - case "bottom": { + case BOTTOM: { return val3; } - case "left": { + case LEFT: { return val4; } default: { @@ -145,9 +162,9 @@ exports.getPositionValue = (positionValues, position) => { }; const borderElements = { - name: "border", - positions: ["top", "right", "bottom", "left"], - lines: ["width", "style", "color"] + name: BORDER, + positions: [TOP, RIGHT, BOTTOM, LEFT], + lines: [WIDTH, STYLE, COLOR] }; const getPropertyItem = (property, properties) => { @@ -160,9 +177,10 @@ const getPropertyItem = (property, properties) => { }; const matchesBorderShorthandValue = (property, value, shorthandValue, opt = {}) => { - const { globalObject } = opt; + const { globalObject, options } = opt; const obj = border.parse(shorthandValue, { - globalObject + globalObject, + options }); if (Object.hasOwn(obj, property)) { return value === obj[property]; @@ -171,16 +189,16 @@ const matchesBorderShorthandValue = (property, value, shorthandValue, opt = {}) }; const replaceBorderShorthandValue = (value, shorthandValue, opt = {}) => { - const { globalObject } = opt; + const { globalObject, options } = opt; const borderFirstInitialKey = border.initialValues.keys().next().value; const borderFirstInitialValue = border.initialValues.get(borderFirstInitialKey); - const valueObj = border.parse(value, { - globalObject - }); + const parseOpt = { + globalObject, + options + }; + const valueObj = border.parse(value, parseOpt); const shorthandObj = shorthandValue - ? border.parse(shorthandValue, { - globalObject - }) + ? border.parse(shorthandValue, parseOpt) : { [borderFirstInitialKey]: borderFirstInitialValue }; @@ -220,16 +238,16 @@ const replacePositionValue = (value, positionValues, position) => { return positionValues.join(" "); } switch (position) { - case "top": { + case TOP: { return [value, val1, val1].join(" "); } - case "right": { + case RIGHT: { return [val1, value, val1, val1].join(" "); } - case "bottom": { + case BOTTOM: { return [val1, val1, value].join(" "); } - case "left": { + case LEFT: { return [val1, val1, val1, value].join(" "); } default: @@ -242,25 +260,25 @@ const replacePositionValue = (value, positionValues, position) => { return replacePositionValue(value, [val1], position); } switch (position) { - case "top": { + case TOP: { if (val1 === value) { return positionValues.join(" "); } return [value, val2, val1].join(" "); } - case "right": { + case RIGHT: { if (val2 === value) { return positionValues.join(" "); } return [val1, value, val1, val2].join(" "); } - case "bottom": { + case BOTTOM: { if (val1 === value) { return positionValues.join(" "); } return [val1, val2, value].join(" "); } - case "left": { + case LEFT: { if (val2 === value) { return positionValues.join(" "); } @@ -276,7 +294,7 @@ const replacePositionValue = (value, positionValues, position) => { return replacePositionValue(value, [val1, val2], position); } switch (position) { - case "top": { + case TOP: { if (val1 === value) { return positionValues.join(" "); } else if (val3 === value) { @@ -284,13 +302,13 @@ const replacePositionValue = (value, positionValues, position) => { } return [value, val2, val3].join(" "); } - case "right": { + case RIGHT: { if (val2 === value) { return positionValues.join(" "); } return [val1, value, val3, val2].join(" "); } - case "bottom": { + case BOTTOM: { if (val3 === value) { return positionValues.join(" "); } else if (val1 === value) { @@ -298,7 +316,7 @@ const replacePositionValue = (value, positionValues, position) => { } return [val1, val2, value].join(" "); } - case "left": { + case LEFT: { if (val2 === value) { return positionValues.join(" "); } @@ -314,13 +332,13 @@ const replacePositionValue = (value, positionValues, position) => { return replacePositionValue(value, [val1, val2, val3], position); } switch (position) { - case "top": { + case TOP: { if (val1 === value) { return positionValues.join(" "); } return [value, val2, val3, val4].join(" "); } - case "right": { + case RIGHT: { if (val2 === value) { return positionValues.join(" "); } else if (val4 === value) { @@ -328,13 +346,13 @@ const replacePositionValue = (value, positionValues, position) => { } return [val1, value, val3, val4].join(" "); } - case "bottom": { + case BOTTOM: { if (val3 === value) { return positionValues.join(" "); } return [val1, val2, value, val4].join(" "); } - case "left": { + case LEFT: { if (val4 === value) { return positionValues.join(" "); } else if (val2 === value) { @@ -350,11 +368,15 @@ const replacePositionValue = (value, positionValues, position) => { } }; -exports.prepareBorderProperties = (property, value, priority, properties, opt = {}) => { +const prepareBorderProperties = (property, value, priority, properties, opt = {}) => { if (typeof property !== "string" || value === null) { return; } - const { globalObject } = opt; + const { globalObject, options } = opt; + const parseOpt = { + globalObject, + options + }; const { lines, name, positions } = borderElements; const [prop1, prop2, prop3] = property.split("-"); if (prop1 !== name) { @@ -374,10 +396,10 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const nameProperty = prop1; // Empty string, global keywords, var(), value of longhands. if (typeof value === "string") { - // longhand properties + // Handle longhand properties if (prop3) { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineProperty = `${prop1}-${prop3}`; const lineItem = getPropertyItem(lineProperty, properties); const positionProperty = `${prop1}-${prop2}`; @@ -404,9 +426,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } else { if ( nameItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, nameItem.value, { - globalObject - }) + !matchesBorderShorthandValue(lineProperty, propertyValue, nameItem.value, parseOpt) ) { nameItem.value = ""; } @@ -415,22 +435,20 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } if ( positionItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, { - globalObject - }) + !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, parseOpt) ) { positionItem.value = ""; } } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); borderItems.set(positionProperty, positionItem); borderItems.set(longhandProperty, longhandItem); - // border-top, border-right, border-bottom, border-left shorthands + // Handle border-top, border-right, border-bottom, border-left shorthands } else if (prop2 && positions.includes(prop2)) { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineWidthProperty = `${prop1}-width`; const lineWidthItem = getPropertyItem(lineWidthProperty, properties); const lineStyleProperty = `${prop1}-style`; @@ -463,19 +481,26 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } else { if ( nameItem.value && - !matchesBorderShorthandValue(property, propertyValue, nameItem.value, { - globalObject - }) + !matchesBorderShorthandValue(property, propertyValue, nameItem.value, parseOpt) ) { nameItem.value = ""; } - if (lineWidthItem.value && isValidPropertyValue(lineWidthProperty, propertyValue)) { + if ( + lineWidthItem.value && + isValidPropertyValue(lineWidthProperty, propertyValue, globalObject) + ) { lineWidthItem.value = propertyValue; } - if (lineStyleItem.value && isValidPropertyValue(lineStyleProperty, propertyValue)) { + if ( + lineStyleItem.value && + isValidPropertyValue(lineStyleProperty, propertyValue, globalObject) + ) { lineStyleItem.value = propertyValue; } - if (lineColorItem.value && isValidPropertyValue(lineColorProperty, propertyValue)) { + if ( + lineColorItem.value && + isValidPropertyValue(lineColorProperty, propertyValue, globalObject) + ) { lineColorItem.value = propertyValue; } } @@ -487,15 +512,15 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineWidthProperty, lineWidthItem); borderItems.set(lineStyleProperty, lineStyleItem); borderItems.set(lineColorProperty, lineColorItem); borderItems.set(positionProperty, positionItem); - // border-width, border-style, border-color + // Handle border-width, border-style, border-color shorthands } else if (prop2 && lines.includes(prop2)) { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineProperty = `${prop1}-${prop2}`; const lineItem = getPropertyItem(lineProperty, properties); lineItem.value = value; @@ -514,9 +539,11 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const longhandProperty = `${prop1}-${position}-${prop2}`; const longhandItem = getPropertyItem(longhandProperty, properties); if (propertyValue) { - positionItem.value = replaceBorderShorthandValue(propertyValue, positionItem.value, { - globalObject - }); + positionItem.value = replaceBorderShorthandValue( + propertyValue, + positionItem.value, + parseOpt + ); } else { positionItem.value = ""; } @@ -526,14 +553,14 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); - // border shorthand + // Handle border shorthand } else { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const propertyValue = hasVarFunc(value) ? "" : value; - imageItem.value = propertyValue ? "none" : ""; + imageItem.value = propertyValue ? NONE : ""; for (const line of lines) { const lineProperty = `${prop1}-${line}`; const lineItem = getPropertyItem(lineProperty, properties); @@ -556,7 +583,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } } borderItems.set(property, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); } // Values of border-width, border-style, border-color } else if (Array.isArray(value)) { @@ -564,15 +591,17 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = return; } const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineProperty = `${prop1}-${prop2}`; const lineItem = getPropertyItem(lineProperty, properties); if (value.length === 1) { const [propertyValue] = value; - if (nameItem.value && propertyValue) { - nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, { - globalObject - }); + if (nameItem.value) { + if (hasVarFunc(nameItem.value)) { + nameItem.value = ""; + } else if (propertyValue) { + nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, parseOpt); + } } } else { nameItem.value = ""; @@ -624,9 +653,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = positionItem.value = replaceBorderShorthandValue( positionValues[position], positionItem.value, - { - globalObject - } + parseOpt ); } const longhandProperty = `${positionProperty}-${prop2}`; @@ -637,17 +664,17 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); // Values of border, border-top, border-right, border-bottom, border-top. } else if (value && typeof value === "object") { - // position shorthands + // Handle position shorthands if (prop2) { if (!positions.includes(prop2)) { return; } const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineWidthProperty = `${prop1}-width`; const lineWidthItem = getPropertyItem(lineWidthProperty, properties); const lineStyleProperty = `${prop1}-style`; @@ -658,11 +685,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const positionItem = getPropertyItem(positionProperty, properties); if (nameItem.value) { for (const positionValue of Object.values(value)) { - if ( - !matchesBorderShorthandValue(property, positionValue, nameItem.value, { - globalObject - }) - ) { + if (!matchesBorderShorthandValue(property, positionValue, nameItem.value, parseOpt)) { nameItem.value = ""; break; } @@ -675,7 +698,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const longhandItem = getPropertyItem(longhandProperty, properties); if (Object.hasOwn(value, longhandProperty)) { const itemValue = value[longhandProperty]; - if (line === "width") { + if (line === WIDTH) { if (lineWidthItem.value) { lineWidthItem.value = replacePositionValue( itemValue, @@ -683,7 +706,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "style") { + } else if (line === STYLE) { if (lineStyleItem.value) { lineStyleItem.value = replacePositionValue( itemValue, @@ -691,7 +714,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "color") { + } else if (line === COLOR) { if (lineColorItem.value) { lineColorItem.value = replacePositionValue( itemValue, @@ -704,7 +727,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = longhandItem.priority = priority; } else { const itemValue = border.initialValues.get(`${prop1}-${line}`); - if (line === "width") { + if (line === WIDTH) { if (lineWidthItem.value) { lineWidthItem.value = replacePositionValue( itemValue, @@ -712,7 +735,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "style") { + } else if (line === STYLE) { if (lineStyleItem.value) { lineStyleItem.value = replacePositionValue( itemValue, @@ -720,7 +743,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "color") { + } else if (line === COLOR) { if (lineColorItem.value) { lineColorItem.value = replacePositionValue( itemValue, @@ -735,15 +758,15 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineWidthProperty, lineWidthItem); borderItems.set(lineStyleProperty, lineStyleItem); borderItems.set(lineColorProperty, lineColorItem); borderItems.set(positionProperty, positionItem); - // border shorthand + // Handle border shorthand } else { const nameItem = getPropertyItem(prop1, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineWidthProperty = `${prop1}-width`; const lineWidthItem = getPropertyItem(lineWidthProperty, properties); const lineStyleProperty = `${prop1}-style`; @@ -753,7 +776,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const propertyValue = Object.values(value).join(" "); nameItem.value = propertyValue; nameItem.priority = priority; - imageItem.value = propertyValue ? "none" : ""; + imageItem.value = propertyValue ? NONE : ""; if (Object.hasOwn(value, lineWidthProperty)) { lineWidthItem.value = value[lineWidthProperty]; } else { @@ -792,7 +815,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(positionProperty, positionItem); } borderItems.set(property, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineWidthProperty, lineWidthItem); borderItems.set(lineStyleProperty, lineStyleItem); borderItems.set(lineColorProperty, lineColorItem); @@ -803,7 +826,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = if (!borderItems.has(name)) { return; } - const borderProperties = new Map([[name, borderItems.get(name)]]); + const borderProps = new Map([[name, borderItems.get(name)]]); for (const line of lines) { const lineProperty = `${name}-${line}`; const lineItem = borderItems.get(lineProperty) ?? @@ -812,7 +835,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(lineProperty, lineItem); + borderProps.set(lineProperty, lineItem); } for (const position of positions) { const positionProperty = `${name}-${position}`; @@ -822,7 +845,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(positionProperty, positionItem); + borderProps.set(positionProperty, positionItem); for (const line of lines) { const longhandProperty = `${name}-${position}-${line}`; const longhandItem = borderItems.get(longhandProperty) ?? @@ -831,16 +854,16 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(longhandProperty, longhandItem); + borderProps.set(longhandProperty, longhandItem); } } - const borderImageItem = borderItems.get(borderImageProperty) ?? { - property: borderImageProperty, + const borderImageItem = borderItems.get(BORDER_IMAGE) ?? { + property: BORDER_IMAGE, value: "", priority: "" }; - borderProperties.set(borderImageProperty, borderImageItem); - return borderProperties; + borderProps.set(BORDER_IMAGE, borderImageItem); + return borderProps; }; const generateBorderLineShorthand = (items, property, prior) => { @@ -849,7 +872,7 @@ const generateBorderLineShorthand = (items, property, prior) => { const { value: itemValue } = item; values.push(itemValue); } - const value = exports.getPositionValue(values); + const value = getPositionValue(values); const priority = prior ? prior : ""; return [property, { property, value, priority }]; }; @@ -892,7 +915,7 @@ const prepareBorderShorthands = (properties) => { for (const [property, { priority, value }] of properties) { const [, positionPart, linePart] = property.split("-"); switch (linePart) { - case "width": { + case WIDTH: { if (priority) { lineWidthPriorItems.set(property, { property, value, priority }); } else { @@ -900,7 +923,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "style": { + case STYLE: { if (priority) { lineStylePriorItems.set(property, { property, value, priority }); } else { @@ -908,7 +931,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "color": { + case COLOR: { if (priority) { lineColorPriorItems.set(property, { property, value, priority }); } else { @@ -919,7 +942,7 @@ const prepareBorderShorthands = (properties) => { default: } switch (positionPart) { - case "top": { + case TOP: { if (priority) { positionTopPriorItems.set(property, { property, value, priority }); } else { @@ -927,7 +950,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "right": { + case RIGHT: { if (priority) { positionRightPriorItems.set(property, { property, value, priority }); } else { @@ -935,7 +958,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "bottom": { + case BOTTOM: { if (priority) { positionBottomPriorItems.set(property, { property, value, priority }); } else { @@ -943,7 +966,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "left": { + case LEFT: { if (priority) { positionLeftPriorItems.set(property, { property, value, priority }); } else { @@ -955,37 +978,37 @@ const prepareBorderShorthands = (properties) => { } } if (lineWidthItems.size === 4) { - const [property, item] = generateBorderLineShorthand(lineWidthItems, "border-width") ?? []; + const [property, item] = generateBorderLineShorthand(lineWidthItems, BORDER_WIDTH) ?? []; if (property && item) { properties.set(property, item); } } else if (lineWidthPriorItems.size === 4) { const [property, item] = - generateBorderLineShorthand(lineWidthPriorItems, "border-width", "important") ?? []; + generateBorderLineShorthand(lineWidthPriorItems, BORDER_WIDTH, "important") ?? []; if (property && item) { properties.set(property, item); } } if (lineStyleItems.size === 4) { - const [property, item] = generateBorderLineShorthand(lineStyleItems, "border-style") ?? []; + const [property, item] = generateBorderLineShorthand(lineStyleItems, BORDER_STYLE) ?? []; if (property && item) { properties.set(property, item); } } else if (lineStylePriorItems.size === 4) { const [property, item] = - generateBorderLineShorthand(lineStylePriorItems, "border-style", "important") ?? []; + generateBorderLineShorthand(lineStylePriorItems, BORDER_STYLE, "important") ?? []; if (property && item) { properties.set(property, item); } } if (lineColorItems.size === 4) { - const [property, item] = generateBorderLineShorthand(lineColorItems, "border-color") ?? []; + const [property, item] = generateBorderLineShorthand(lineColorItems, BORDER_COLOR) ?? []; if (property && item) { properties.set(property, item); } } else if (lineColorPriorItems.size === 4) { const [property, item] = - generateBorderLineShorthand(lineColorPriorItems, "border-color", "important") ?? []; + generateBorderLineShorthand(lineColorPriorItems, BORDER_COLOR, "important") ?? []; if (property && item) { properties.set(property, item); } @@ -993,12 +1016,12 @@ const prepareBorderShorthands = (properties) => { const nameItems = []; const namePriorItems = []; if (positionTopItems.size === 3) { - const [property, item] = generateBorderPositionShorthand(positionTopItems, "border-top") ?? []; + const [property, item] = generateBorderPositionShorthand(positionTopItems, BORDER_TOP) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1006,12 +1029,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionTopPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionTopPriorItems, "border-top", "important") ?? []; + generateBorderPositionShorthand(positionTopPriorItems, BORDER_TOP, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; namePriorItems.push(itemValue); } @@ -1020,12 +1043,12 @@ const prepareBorderShorthands = (properties) => { } if (positionRightItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionRightItems, "border-right") ?? []; + generateBorderPositionShorthand(positionRightItems, BORDER_RIGHT) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1033,12 +1056,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionRightPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionRightPriorItems, "border-right", "important") ?? []; + generateBorderPositionShorthand(positionRightPriorItems, BORDER_RIGHT, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1047,12 +1070,12 @@ const prepareBorderShorthands = (properties) => { } if (positionBottomItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionBottomItems, "border-bottom") ?? []; + generateBorderPositionShorthand(positionBottomItems, BORDER_BOTTOM) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1060,12 +1083,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionBottomPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionBottomPriorItems, "border-bottom", "important") ?? []; + generateBorderPositionShorthand(positionBottomPriorItems, BORDER_BOTTOM, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1073,13 +1096,12 @@ const prepareBorderShorthands = (properties) => { } } if (positionLeftItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionLeftItems, "border-left") ?? []; + const [property, item] = generateBorderPositionShorthand(positionLeftItems, BORDER_LEFT) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1087,12 +1109,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionLeftPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionLeftPriorItems, "border-left", "important") ?? []; + generateBorderPositionShorthand(positionLeftPriorItems, BORDER_LEFT, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1101,33 +1123,32 @@ const prepareBorderShorthands = (properties) => { } const mixedPriorities = nameItems.length && namePriorItems.length; const imageItem = { - property: borderImageProperty, - value: "none", + property: BORDER_IMAGE, + value: NONE, priority: "" }; if (nameItems.length === 4) { - const [property, item] = generateBorderNameShorthand(nameItems, "border") ?? []; + const [property, item] = generateBorderNameShorthand(nameItems, BORDER) ?? []; if (property && item) { properties.set(property, item); - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } } else if (namePriorItems.length === 4) { - const [property, item] = - generateBorderNameShorthand(namePriorItems, "border", "important") ?? []; + const [property, item] = generateBorderNameShorthand(namePriorItems, BORDER, "important") ?? []; if (property && item) { properties.set(property, item); - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } - } else if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + } else if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { if (mixedPriorities) { - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } else { - properties.delete(borderImageProperty); + properties.delete(BORDER_IMAGE); } } } @@ -1149,25 +1170,29 @@ const prepareBorderShorthands = (properties) => { } return new Map([...items, ...priorItems]); } - if (properties.has(borderImageProperty)) { - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + if (properties.has(BORDER_IMAGE)) { + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } return properties; }; -exports.prepareProperties = (properties, opt = {}) => { - const { globalObject } = opt; +const prepareProperties = (properties, opt = {}) => { + const { globalObject, options } = opt; + const parseOpt = { + globalObject, + options + }; const { positions } = borderElements; const parsedProperties = new Map(); const prepareShorthands = new Map(); - const borderProperties = new Map(); + const borderProps = new Map(); for (const [property, item] of properties) { const { value, priority } = item; - const { logicalPropertyGroup: shorthandProperty } = implementedProperties.get(property) ?? {}; - if (exports.borderProperties.has(property)) { - borderProperties.set(property, { property, value, priority }); - } else if (exports.shorthandProperties.has(shorthandProperty)) { + const { logicalPropertyGroup: shorthandProperty } = propertyList.get(property) ?? {}; + if (borderProperties.has(property)) { + borderProps.set(property, { property, value, priority }); + } else if (shorthandProperties.has(shorthandProperty)) { if (!prepareShorthands.has(shorthandProperty)) { prepareShorthands.set(shorthandProperty, new Map()); } @@ -1186,11 +1211,9 @@ exports.prepareProperties = (properties, opt = {}) => { prepareShorthands.set(shorthandProperty, longhandItems); } parsedProperties.set(property, item); - } else if (exports.shorthandProperties.has(property)) { - const shorthandItem = exports.shorthandProperties.get(property); - const parsedValues = shorthandItem.parse(value, { - globalObject - }); + } else if (shorthandProperties.has(property)) { + const shorthandItem = shorthandProperties.get(property); + const parsedValues = shorthandItem.parse(value, parseOpt); let omitShorthandProperty = false; if (Array.isArray(parsedValues)) { const [parsedValue] = parsedValues; @@ -1204,7 +1227,7 @@ exports.prepareProperties = (properties, opt = {}) => { } } const { position } = longhandItem; - const longhandValue = exports.getPositionValue([parsedValue], position); + const longhandValue = getPositionValue([parsedValue], position); parsedProperties.set(longhandProperty, { property: longhandProperty, value: longhandValue, @@ -1232,7 +1255,13 @@ exports.prepareProperties = (properties, opt = {}) => { } } if (!omitShorthandProperty) { - parsedProperties.set(property, { property, value, priority }); + if (property === BACKGROUND) { + if (hasVarFunc(value) || priority) { + parsedProperties.set(property, { property, value, priority }); + } + } else { + parsedProperties.set(property, { property, value, priority }); + } } } else { parsedProperties.set(property, { property, value, priority }); @@ -1240,7 +1269,7 @@ exports.prepareProperties = (properties, opt = {}) => { } if (prepareShorthands.size) { for (const [property, item] of prepareShorthands) { - const shorthandItem = exports.shorthandProperties.get(property); + const shorthandItem = shorthandProperties.get(property); if (item.size === shorthandItem.shorthandFor.size) { if (shorthandItem.position) { const positionValues = []; @@ -1251,7 +1280,7 @@ exports.prepareProperties = (properties, opt = {}) => { priority = longhandPriority; } } - const value = exports.getPositionValue(positionValues, shorthandItem.position); + const value = getPositionValue(positionValues, shorthandItem.position); parsedProperties.set(property, { property, value, @@ -1261,15 +1290,13 @@ exports.prepareProperties = (properties, opt = {}) => { } } } - if (borderProperties.size) { + if (borderProps.size) { const longhandProperties = new Map(); - for (const [property, item] of borderProperties) { - if (exports.shorthandProperties.has(property)) { + for (const [property, item] of borderProps) { + if (shorthandProperties.has(property)) { const { value, priority } = item; - if (property === "border") { - const lineItems = border.parse(value, { - globalObject - }); + if (property === BORDER) { + const lineItems = border.parse(value, parseOpt); for (const [key, initialValue] of border.initialValues) { if (!Object.hasOwn(lineItems, key)) { lineItems[key] = initialValue; @@ -1297,22 +1324,20 @@ exports.prepareProperties = (properties, opt = {}) => { } } if (value) { - longhandProperties.set(borderImageProperty, { - property: borderImageProperty, - value: "none", + longhandProperties.set(BORDER_IMAGE, { + property: BORDER_IMAGE, + value: NONE, priority }); } } else { - const shorthandItem = exports.shorthandProperties.get(property); - const parsedItem = shorthandItem.parse(value, { - globalObject - }); + const shorthandItem = shorthandProperties.get(property); + const parsedItem = shorthandItem.parse(value, parseOpt); if (Array.isArray(parsedItem)) { const [namePart, linePart] = property.split("-"); for (const position of positions) { const longhandProperty = `${namePart}-${position}-${linePart}`; - const longhandValue = exports.getPositionValue(parsedItem, position); + const longhandValue = getPositionValue(parsedItem, position); const longhandItem = { property: longhandProperty, value: longhandValue, @@ -1371,7 +1396,7 @@ exports.prepareProperties = (properties, opt = {}) => { return parsedProperties; }; -exports.normalizeBorderProperties = (properties) => { +const normalizeProperties = (properties) => { const { lines, name, positions } = borderElements; if (properties.has(name)) { for (const line of lines) { @@ -1415,3 +1440,12 @@ exports.normalizeBorderProperties = (properties) => { } return properties; }; + +module.exports = { + borderProperties, + getPositionValue, + normalizeProperties, + prepareBorderProperties, + prepareProperties, + shorthandProperties +}; diff --git a/lib/parsers.js b/lib/parsers.js index 8527e321..3a062bc9 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -6,6 +6,7 @@ const { } = require("@asamuzakjp/css-color"); const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree"); const csstree = require("css-tree"); +const { getCache, setCache } = require("./utils/cache"); const { asciiLowercase } = require("./utils/strings"); // CSS global keywords @@ -73,7 +74,7 @@ const varContainedRegEx = /(?<=[*/\s(])var\(/; const cssTree = csstree.fork(syntaxes); // Prepare stringified value. -exports.prepareValue = (value, globalObject = globalThis) => { +const prepareValue = (value, globalObject = globalThis) => { // `null` is converted to an empty string. // @see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString if (value === null) { @@ -100,30 +101,27 @@ exports.prepareValue = (value, globalObject = globalThis) => { }; // Value is a global keyword. -exports.isGlobalKeyword = (val) => { +const isGlobalKeyword = (val) => { return GLOBAL_KEY.includes(asciiLowercase(val)); }; // Value starts with and/or contains CSS var() function. -exports.hasVarFunc = (val) => { +const hasVarFunc = (val) => { return varRegEx.test(val) || varContainedRegEx.test(val); }; // Value starts with and/or contains CSS calc() related functions. -exports.hasCalcFunc = (val) => { +const hasCalcFunc = (val) => { return calcRegEx.test(val) || calcContainedRegEx.test(val); }; -// Splits value into an array. -// @see https://github.com/asamuzaK/cssColor/blob/main/src/js/util.ts -exports.splitValue = splitValue; - // Parse CSS to AST. -exports.parseCSS = (val, opt, toObject = false) => { +const parseCSS = (val, opt = {}, toObject = false) => { + const { globalObject, options } = opt; if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val, globalObject); } - const ast = cssTree.parse(val, opt); + const ast = cssTree.parse(val, options); if (toObject) { return cssTree.toPlainObject(ast); } @@ -132,9 +130,9 @@ exports.parseCSS = (val, opt, toObject = false) => { // Value is a valid property value. // Returns `false` for custom property and/or var(). -exports.isValidPropertyValue = (prop, val) => { +const isValidPropertyValue = (prop, val, globalObject) => { if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val, globalObject); } if (val === "") { return true; @@ -150,8 +148,12 @@ exports.isValidPropertyValue = (prop, val) => { } let ast; try { - ast = exports.parseCSS(val, { - context: "value" + // TBD: use cache? + ast = parseCSS(val, { + globalObject, + options: { + context: "value" + } }); } catch { return false; @@ -160,15 +162,30 @@ exports.isValidPropertyValue = (prop, val) => { return error === null && matched !== null; }; +const defaultOptions = { + format: "specifiedValue" +}; + // Simplify / resolve math functions. -exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { +const resolveCalc = (val, opt = {}) => { + const { globalObject, options } = opt; if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val, globalObject); } - if (val === "" || exports.hasVarFunc(val) || !exports.hasCalcFunc(val)) { + if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) { return val; } - const obj = exports.parseCSS(val, { context: "value" }, true); + // TBD: use cache? + const obj = parseCSS( + val, + { + globalObject, + options: { + context: "value" + } + }, + true + ); if (!obj?.children) { return; } @@ -180,9 +197,10 @@ exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { const value = cssTree .generate(item) .replace(/\)(?!\)|\s|,)/g, ") ") + .replace(/,(?!\s)/g, ", ") .trim(); if (calcNameRegEx.test(itemName)) { - const newValue = cssCalc(value, opt); + const newValue = cssCalc(value, options ?? defaultOptions); values.push(newValue); } else { values.push(value); @@ -197,15 +215,13 @@ exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { }; // Parse property value. Returns string or array of parsed object. -exports.parsePropertyValue = (prop, val, opt = {}) => { +const parsePropertyValue = (prop, val, opt = {}) => { const { caseSensitive, globalObject, inArray } = opt; - val = exports.prepareValue(val, globalObject); - if (val === "" || exports.hasVarFunc(val)) { + val = prepareValue(val, globalObject); + if (val === "" || hasVarFunc(val)) { return val; - } else if (exports.hasCalcFunc(val)) { - const calculatedValue = exports.resolveCalc(val, { - format: "specifiedValue" - }); + } else if (hasCalcFunc(val)) { + const calculatedValue = resolveCalc(val, opt); if (typeof calculatedValue !== "string") { return; } @@ -237,8 +253,19 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { return; } try { - const ast = exports.parseCSS(val, { - context: "value" + let cacheKey = ""; + if (inArray) { + cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`; + const cachedValues = getCache(cacheKey); + if (Array.isArray(cachedValues)) { + return cachedValues; + } + } + const ast = parseCSS(val, { + globalObject, + options: { + context: "value" + } }); const { error, matched } = cssTree.lexer.matchProperty(prop, ast); if (error || !matched) { @@ -285,7 +312,7 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { type: "Calc", name: "calc", isNumber: false, - value: `${asciiLowercase(itemValue)}`, + value: asciiLowercase(itemValue), raw }); } @@ -324,6 +351,9 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { } } } + if (cacheKey) { + setCache(cacheKey, parsedValues); + } return parsedValues; } } catch { @@ -333,7 +363,7 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { }; // Parse . -exports.parseNumber = (val, opt = {}) => { +const parseNumber = (val, opt = {}) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "Number") { @@ -356,7 +386,7 @@ exports.parseNumber = (val, opt = {}) => { }; // Parse . -exports.parseLength = (val, opt = {}) => { +const parseLength = (val, opt = {}) => { const [item] = val; const { type, value, unit } = item ?? {}; if (type !== "Dimension" && !(type === "Number" && value === "0")) { @@ -378,12 +408,12 @@ exports.parseLength = (val, opt = {}) => { if (num === 0 && !unit) { return `${num}px`; } else if (unit) { - return `${num}${asciiLowercase(unit)}`; + return `${num}${unit}`; } }; // Parse . -exports.parsePercentage = (val, opt = {}) => { +const parsePercentage = (val, opt = {}) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "Percentage" && !(type === "Number" && value === "0")) { @@ -409,7 +439,7 @@ exports.parsePercentage = (val, opt = {}) => { }; // Parse . -exports.parseLengthPercentage = (val, opt = {}) => { +const parseLengthPercentage = (val, opt = {}) => { const [item] = val; const { type, value, unit } = item ?? {}; if (type !== "Dimension" && type !== "Percentage" && !(type === "Number" && value === "0")) { @@ -432,7 +462,7 @@ exports.parseLengthPercentage = (val, opt = {}) => { if (/deg|g?rad|turn/i.test(unit)) { return; } - return `${num}${asciiLowercase(unit)}`; + return `${num}${unit}`; } else if (type === "Percentage") { return `${num}%`; } else if (num === 0) { @@ -441,7 +471,7 @@ exports.parseLengthPercentage = (val, opt = {}) => { }; // Parse . -exports.parseAngle = (val) => { +const parseAngle = (val) => { const [item] = val; const { type, value, unit } = item ?? {}; if (type !== "Dimension" && !(type === "Number" && value === "0")) { @@ -452,14 +482,14 @@ exports.parseAngle = (val) => { if (!/^(?:deg|g?rad|turn)$/i.test(unit)) { return; } - return `${num}${asciiLowercase(unit)}`; + return `${num}${unit}`; } else if (num === 0) { return `${num}deg`; } }; // Parse . -exports.parseUrl = (val) => { +const parseURL = (val) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "Url") { @@ -470,7 +500,7 @@ exports.parseUrl = (val) => { }; // Parse . -exports.parseString = (val) => { +const parseString = (val) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "String") { @@ -481,23 +511,19 @@ exports.parseString = (val) => { }; // Parse . -exports.parseColor = (val) => { +const parseColor = (val, opt = defaultOptions) => { const [item] = val; const { name, type, value } = item ?? {}; switch (type) { case "Function": { - const res = resolveColor(`${name}(${value})`, { - format: "specifiedValue" - }); + const res = resolveColor(`${name}(${value})`, opt); if (res) { return res; } break; } case "Hash": { - const res = resolveColor(`#${value}`, { - format: "specifiedValue" - }); + const res = resolveColor(`#${value}`, opt); if (res) { return res; } @@ -507,9 +533,7 @@ exports.parseColor = (val) => { if (SYS_COLOR.includes(name)) { return name; } - const res = resolveColor(name, { - format: "specifiedValue" - }); + const res = resolveColor(name, opt); if (res) { return res; } @@ -520,16 +544,35 @@ exports.parseColor = (val) => { }; // Parse . -exports.parseGradient = (val) => { +const parseGradient = (val, opt = defaultOptions) => { const [item] = val; const { name, type, value } = item ?? {}; if (type !== "Function") { return; } - const res = resolveGradient(`${name}(${value})`, { - format: "specifiedValue" - }); + const res = resolveGradient(`${name}(${value})`, opt); if (res) { return res; } }; + +module.exports = { + hasCalcFunc, + hasVarFunc, + isGlobalKeyword, + isValidPropertyValue, + parseAngle, + parseCSS, + parseColor, + parseGradient, + parseLength, + parseLengthPercentage, + parseNumber, + parsePercentage, + parsePropertyValue, + parseString, + parseURL, + prepareValue, + resolveCalc, + splitValue +}; diff --git a/lib/properties/background.js b/lib/properties/background.js index c94327bf..bf4c26f0 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -34,13 +34,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; + const { globalObject, options } = opt; if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { - v = parsers.resolveCalc(v); + v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v)) { + if (!parsers.isValidPropertyValue(property, v, globalObject)) { return; } const values = parsers.splitValue(v, { @@ -85,12 +85,12 @@ module.exports.parse = function parse(v, opt = {}) { for (const part of parts1) { let partValid = false; for (const [longhand, value] of module.exports.shorthandFor) { - if (parsers.isValidPropertyValue(longhand, part)) { + if (parsers.isValidPropertyValue(longhand, part, globalObject)) { partValid = true; switch (longhand) { case "background-clip": case "background-origin": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgBox.push(parsedValue); } @@ -100,31 +100,25 @@ module.exports.parse = function parse(v, opt = {}) { if (i !== values.length - 1) { return; } - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } break; } case "background-position": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgPosition.push(parsedValue); - } + bgPosition.push(part); break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgRepeat.push(parsedValue); - } + bgRepeat.push(part); break; } case "background-size": { break; } default: { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -141,12 +135,12 @@ module.exports.parse = function parse(v, opt = {}) { for (const part of parts2) { let partValid = false; for (const [longhand, value] of module.exports.shorthandFor) { - if (parsers.isValidPropertyValue(longhand, part)) { + if (parsers.isValidPropertyValue(longhand, part, globalObject)) { partValid = true; switch (longhand) { case "background-clip": case "background-origin": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgBox.push(parsedValue); } @@ -156,7 +150,7 @@ module.exports.parse = function parse(v, opt = {}) { if (i !== l - 1) { return; } - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -166,21 +160,15 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgRepeat.push(parsedValue); - } + bgRepeat.push(part); break; } case "background-size": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgSize.push(parsedValue); - } + bgSize.push(part); break; } default: { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -195,21 +183,21 @@ module.exports.parse = function parse(v, opt = {}) { } if (bgPosition.length) { const { parse: parser } = module.exports.shorthandFor.get("background-position"); - const value = parser(bgPosition.join(" "), { globalObject }); + const value = parser(bgPosition.join(" "), { globalObject, options }); if (value) { bg["background-position"] = value; } } if (bgSize.length) { const { parse: parser } = module.exports.shorthandFor.get("background-size"); - const value = parser(bgSize.join(" "), { globalObject }); + const value = parser(bgSize.join(" "), { globalObject, options }); if (value) { bg["background-size"] = value; } } if (bgRepeat.length) { const { parse: parser } = module.exports.shorthandFor.get("background-repeat"); - const value = parser(bgRepeat.join(" "), { globalObject }); + const value = parser(bgRepeat.join(" "), { globalObject, options }); if (value) { bg["background-repeat"] = value; } @@ -248,7 +236,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const bgValues = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (!Array.isArray(bgValues)) { return; diff --git a/lib/properties/backgroundAttachment.js b/lib/properties/backgroundAttachment.js index e8ee4565..1d440e66 100644 --- a/lib/properties/backgroundAttachment.js +++ b/lib/properties/backgroundAttachment.js @@ -6,15 +6,16 @@ const property = "background-attachment"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundClip.js b/lib/properties/backgroundClip.js index 5eb2fe20..40aec9e7 100644 --- a/lib/properties/backgroundClip.js +++ b/lib/properties/backgroundClip.js @@ -6,15 +6,16 @@ const property = "background-clip"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundColor.js b/lib/properties/backgroundColor.js index 8dfe76fb..f06bd8b0 100644 --- a/lib/properties/backgroundColor.js +++ b/lib/properties/backgroundColor.js @@ -6,12 +6,13 @@ const property = "background-color"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -21,7 +22,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -37,7 +38,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundImage.js b/lib/properties/backgroundImage.js index 07a4e9d9..2eb41b17 100644 --- a/lib/properties/backgroundImage.js +++ b/lib/properties/backgroundImage.js @@ -6,15 +6,16 @@ const property = "background-image"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -26,7 +27,7 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Url": { - const parsedValue = parsers.parseUrl(value); + const parsedValue = parsers.parseURL(value, options); if (!parsedValue) { return; } @@ -34,7 +35,7 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseGradient(value); + const parsedValue = parsers.parseGradient(value, options); if (!parsedValue) { return; } @@ -60,7 +61,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundOrigin.js b/lib/properties/backgroundOrigin.js index 6c3cd4bd..604b8d52 100644 --- a/lib/properties/backgroundOrigin.js +++ b/lib/properties/backgroundOrigin.js @@ -6,15 +6,16 @@ const property = "background-origin"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundPosition.js b/lib/properties/backgroundPosition.js index f99b9bc2..96b24e2c 100644 --- a/lib/properties/backgroundPosition.js +++ b/lib/properties/backgroundPosition.js @@ -11,10 +11,10 @@ const keywordsY = ["center", ...keyY]; const keywords = ["center", ...keyX, ...keyY]; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); @@ -22,6 +22,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -30,7 +31,9 @@ module.exports.parse = function parse(v, opt = {}) { case 1: { const [part1] = value; const val1 = - part1.type === "Identifier" ? part1.name : parsers.parseLengthPercentage([part1]); + part1.type === "Identifier" + ? part1.name + : parsers.parseLengthPercentage([part1], options); if (val1) { if (val1 === "center") { parsedValue = `${val1} ${val1}`; @@ -45,9 +48,13 @@ module.exports.parse = function parse(v, opt = {}) { case 2: { const [part1, part2] = value; const val1 = - part1.type === "Identifier" ? part1.name : parsers.parseLengthPercentage([part1]); + part1.type === "Identifier" + ? part1.name + : parsers.parseLengthPercentage([part1], options); const val2 = - part2.type === "Identifier" ? part2.name : parsers.parseLengthPercentage([part2]); + part2.type === "Identifier" + ? part2.name + : parsers.parseLengthPercentage([part2], options); if (val1 && val2) { if (keywordsX.includes(val1) && keywordsY.includes(val2)) { parsedValue = `${val1} ${val2}`; @@ -71,9 +78,13 @@ module.exports.parse = function parse(v, opt = {}) { const [part1, part2, part3] = value; const val1 = part1.type === "Identifier" && part1.name; const val2 = - part2.type === "Identifier" ? part2.name : parsers.parseLengthPercentage([part2]); + part2.type === "Identifier" + ? part2.name + : parsers.parseLengthPercentage([part2], options); const val3 = - part3.type === "Identifier" ? part3.name : parsers.parseLengthPercentage([part3]); + part3.type === "Identifier" + ? part3.name + : parsers.parseLengthPercentage([part3], options); if (val1 && val2 && val3) { let posX = ""; let offX = ""; @@ -121,9 +132,9 @@ module.exports.parse = function parse(v, opt = {}) { case 4: { const [part1, part2, part3, part4] = value; const val1 = part1.type === "Identifier" && part1.name; - const val2 = parsers.parseLengthPercentage([part2]); + const val2 = parsers.parseLengthPercentage([part2], options); const val3 = part3.type === "Identifier" && part3.name; - const val4 = parsers.parseLengthPercentage([part4]); + const val4 = parsers.parseLengthPercentage([part4], options); if (val1 && val2 && val3 && val4) { let posX = ""; let offX = ""; @@ -175,7 +186,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundRepeat.js b/lib/properties/backgroundRepeat.js index 30fca2a8..5440421f 100644 --- a/lib/properties/backgroundRepeat.js +++ b/lib/properties/backgroundRepeat.js @@ -6,10 +6,10 @@ const property = "background-repeat"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); @@ -17,6 +17,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -71,7 +72,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/backgroundSize.js b/lib/properties/backgroundSize.js index bd59a1b7..ffb252ce 100644 --- a/lib/properties/backgroundSize.js +++ b/lib/properties/backgroundSize.js @@ -6,10 +6,10 @@ const property = "background-size"; const shorthand = "background"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); @@ -17,6 +17,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -36,7 +37,7 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLengthPercentage(value); + const parsedValue = parsers.parseLengthPercentage(value, options); if (!parsedValue) { return; } @@ -102,7 +103,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/border.js b/lib/properties/border.js index 9ce6efb2..a0b6078e 100644 --- a/lib/properties/border.js +++ b/lib/properties/border.js @@ -31,15 +31,16 @@ module.exports.positionShorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "" || parsers.hasVarFunc(v)) { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -57,9 +58,13 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-width")) { return; } - const parsedValue = parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, optVal] of Object.entries(options)) { + parseOpt[key] = optVal; + } + const parsedValue = parsers.parseLength(value, parseOpt); if (!parsedValue) { return; } @@ -70,7 +75,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-color")) { return; } - const parsedValue = parsers.parseColor(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -84,7 +89,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-color")) { return; } - const parsedValue = parsers.parseColor(`#${itemValue}`); + const parsedValue = parsers.parseColor(`#${itemValue}`, options); if (!parsedValue) { return; } @@ -92,19 +97,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-width", name)) { + if (parsers.isValidPropertyValue("border-width", name, globalObject)) { if (parsedValues.has("border-width")) { return; } parsedValues.set("border-width", name); break; - } else if (parsers.isValidPropertyValue("border-style", name)) { + } else if (parsers.isValidPropertyValue("border-style", name, globalObject)) { if (parsedValues.has("border-style")) { return; } parsedValues.set("border-style", name); break; - } else if (parsers.isValidPropertyValue("border-color", name)) { + } else if (parsers.isValidPropertyValue("border-color", name, globalObject)) { if (parsedValues.has("border-color")) { return; } @@ -148,7 +153,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/borderBottom.js b/lib/properties/borderBottom.js index 2a82a047..3b79ec32 100644 --- a/lib/properties/borderBottom.js +++ b/lib/properties/borderBottom.js @@ -21,15 +21,16 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -47,9 +48,13 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-bottom-width")) { return; } - const parsedValue = parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, optVal] of Object.entries(options)) { + parseOpt[key] = optVal; + } + const parsedValue = parsers.parseLength(value, parseOpt); if (!parsedValue) { return; } @@ -60,7 +65,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-bottom-color")) { return; } - const parsedValue = parsers.parseColor(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -74,7 +79,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-bottom-color")) { return; } - const parsedValue = parsers.parseColor(`#${itemValue}`); + const parsedValue = parsers.parseColor(`#${itemValue}`, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-bottom-width", name)) { + if (parsers.isValidPropertyValue("border-bottom-width", name, globalObject)) { if (parsedValues.has("border-bottom-width")) { return; } parsedValues.set("border-bottom-width", name); break; - } else if (parsers.isValidPropertyValue("border-bottom-style", name)) { + } else if (parsers.isValidPropertyValue("border-bottom-style", name, globalObject)) { if (parsedValues.has("border-bottom-style")) { return; } parsedValues.set("border-bottom-style", name); break; - } else if (parsers.isValidPropertyValue("border-bottom-color", name)) { + } else if (parsers.isValidPropertyValue("border-bottom-color", name, globalObject)) { if (parsedValues.has("border-bottom-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderBottomColor.js b/lib/properties/borderBottomColor.js index 015a60fd..fc54da4c 100644 --- a/lib/properties/borderBottomColor.js +++ b/lib/properties/borderBottomColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-bottom"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderBottomStyle.js b/lib/properties/borderBottomStyle.js index fc865b66..ad596756 100644 --- a/lib/properties/borderBottomStyle.js +++ b/lib/properties/borderBottomStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-bottom"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderBottomWidth.js b/lib/properties/borderBottomWidth.js index 5e6b682a..4305290f 100644 --- a/lib/properties/borderBottomWidth.js +++ b/lib/properties/borderBottomWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-bottom"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderCollapse.js b/lib/properties/borderCollapse.js index 55e56866..c8f49856 100644 --- a/lib/properties/borderCollapse.js +++ b/lib/properties/borderCollapse.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "border-collapse"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -34,7 +35,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/borderColor.js b/lib/properties/borderColor.js index 156bdd9e..6cb17df4 100644 --- a/lib/properties/borderColor.js +++ b/lib/properties/borderColor.js @@ -17,12 +17,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -40,7 +41,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - const parsedValue = parsers.parseColor([value]); + const parsedValue = parsers.parseColor([value], options); if (!parsedValue) { return; } @@ -98,7 +99,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeft.js b/lib/properties/borderLeft.js index 4daa922b..1df59aa1 100644 --- a/lib/properties/borderLeft.js +++ b/lib/properties/borderLeft.js @@ -21,15 +21,16 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -47,9 +48,13 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-left-width")) { return; } - const parsedValue = parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, optVal] of Object.entries(options)) { + parseOpt[key] = optVal; + } + const parsedValue = parsers.parseLength(value, parseOpt); if (!parsedValue) { return; } @@ -60,7 +65,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-left-color")) { return; } - const parsedValue = parsers.parseColor(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -74,7 +79,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-left-color")) { return; } - const parsedValue = parsers.parseColor(`#${itemValue}`); + const parsedValue = parsers.parseColor(`#${itemValue}`, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-left-width", name)) { + if (parsers.isValidPropertyValue("border-left-width", name, globalObject)) { if (parsedValues.has("border-left-width")) { return; } parsedValues.set("border-left-width", name); break; - } else if (parsers.isValidPropertyValue("border-left-style", name)) { + } else if (parsers.isValidPropertyValue("border-left-style", name, globalObject)) { if (parsedValues.has("border-left-style")) { return; } parsedValues.set("border-left-style", name); break; - } else if (parsers.isValidPropertyValue("border-left-color", name)) { + } else if (parsers.isValidPropertyValue("border-left-color", name, globalObject)) { if (parsedValues.has("border-left-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeftColor.js b/lib/properties/borderLeftColor.js index d9c6b498..1f179628 100644 --- a/lib/properties/borderLeftColor.js +++ b/lib/properties/borderLeftColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-left"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeftStyle.js b/lib/properties/borderLeftStyle.js index 115106a1..d54928a9 100644 --- a/lib/properties/borderLeftStyle.js +++ b/lib/properties/borderLeftStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-left"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeftWidth.js b/lib/properties/borderLeftWidth.js index 51e20652..72e3bb83 100644 --- a/lib/properties/borderLeftWidth.js +++ b/lib/properties/borderLeftWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-left"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRight.js b/lib/properties/borderRight.js index 18a9ab47..2ecfeb14 100644 --- a/lib/properties/borderRight.js +++ b/lib/properties/borderRight.js @@ -21,15 +21,16 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -47,9 +48,13 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-right-width")) { return; } - const parsedValue = parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, optVal] of Object.entries(options)) { + parseOpt[key] = optVal; + } + const parsedValue = parsers.parseLength(value, parseOpt); if (!parsedValue) { return; } @@ -60,7 +65,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-right-color")) { return; } - const parsedValue = parsers.parseColor(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -74,7 +79,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-right-color")) { return; } - const parsedValue = parsers.parseColor(`#${itemValue}`); + const parsedValue = parsers.parseColor(`#${itemValue}`, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-right-width", name)) { + if (parsers.isValidPropertyValue("border-right-width", name, globalObject)) { if (parsedValues.has("border-right-width")) { return; } parsedValues.set("border-right-width", name); break; - } else if (parsers.isValidPropertyValue("border-right-style", name)) { + } else if (parsers.isValidPropertyValue("border-right-style", name, globalObject)) { if (parsedValues.has("border-right-style")) { return; } parsedValues.set("border-right-style", name); break; - } else if (parsers.isValidPropertyValue("border-right-color", name)) { + } else if (parsers.isValidPropertyValue("border-right-color", name, globalObject)) { if (parsedValues.has("border-right-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRightColor.js b/lib/properties/borderRightColor.js index 807b6df4..857ac14c 100644 --- a/lib/properties/borderRightColor.js +++ b/lib/properties/borderRightColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-right"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRightStyle.js b/lib/properties/borderRightStyle.js index 0d343c3b..ea43a6e3 100644 --- a/lib/properties/borderRightStyle.js +++ b/lib/properties/borderRightStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-right"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRightWidth.js b/lib/properties/borderRightWidth.js index f4c5147d..beb95674 100644 --- a/lib/properties/borderRightWidth.js +++ b/lib/properties/borderRightWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-right"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderSpacing.js b/lib/properties/borderSpacing.js index d33df5a3..fee04259 100644 --- a/lib/properties/borderSpacing.js +++ b/lib/properties/borderSpacing.js @@ -5,19 +5,20 @@ const parsers = require("../parsers"); const property = "border-spacing"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { switch (value.length) { case 1: { const [part1] = value; - const val1 = parsers.parseLength([part1]); + const val1 = parsers.parseLength([part1], options); if (val1) { return val1; } @@ -25,8 +26,8 @@ module.exports.parse = function parse(v, opt = {}) { } case 2: { const [part1, part2] = value; - const val1 = parsers.parseLength([part1]); - const val2 = parsers.parseLength([part2]); + const val1 = parsers.parseLength([part1], options); + const val2 = parsers.parseLength([part2], options); if (val1 && val2) { return `${val1} ${val2}`; } @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/borderStyle.js b/lib/properties/borderStyle.js index ebeddbeb..c9c9f032 100644 --- a/lib/properties/borderStyle.js +++ b/lib/properties/borderStyle.js @@ -17,12 +17,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -98,7 +99,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTop.js b/lib/properties/borderTop.js index d4fb339f..38ed4267 100644 --- a/lib/properties/borderTop.js +++ b/lib/properties/borderTop.js @@ -21,15 +21,16 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -47,9 +48,13 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-top-width")) { return; } - const parsedValue = parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, optVal] of Object.entries(options)) { + parseOpt[key] = optVal; + } + const parsedValue = parsers.parseLength(value, parseOpt); if (!parsedValue) { return; } @@ -60,7 +65,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-top-color")) { return; } - const parsedValue = parsers.parseColor(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -74,7 +79,7 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.has("border-top-color")) { return; } - const parsedValue = parsers.parseColor(`#${itemValue}`); + const parsedValue = parsers.parseColor(`#${itemValue}`, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-top-width", name)) { + if (parsers.isValidPropertyValue("border-top-width", name, globalObject)) { if (parsedValues.has("border-top-width")) { return; } parsedValues.set("border-top-width", name); break; - } else if (parsers.isValidPropertyValue("border-top-style", name)) { + } else if (parsers.isValidPropertyValue("border-top-style", name, globalObject)) { if (parsedValues.has("border-top-style")) { return; } parsedValues.set("border-top-style", name); break; - } else if (parsers.isValidPropertyValue("border-top-color", name)) { + } else if (parsers.isValidPropertyValue("border-top-color", name, globalObject)) { if (parsedValues.has("border-top-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTopColor.js b/lib/properties/borderTopColor.js index 8acfa672..be76ca59 100644 --- a/lib/properties/borderTopColor.js +++ b/lib/properties/borderTopColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-top"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTopStyle.js b/lib/properties/borderTopStyle.js index a90c2f68..0134de2c 100644 --- a/lib/properties/borderTopStyle.js +++ b/lib/properties/borderTopStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-top"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTopWidth.js b/lib/properties/borderTopWidth.js index f97d124c..74eeaf5d 100644 --- a/lib/properties/borderTopWidth.js +++ b/lib/properties/borderTopWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-top"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderWidth.js b/lib/properties/borderWidth.js index b34496fa..92f27dd4 100644 --- a/lib/properties/borderWidth.js +++ b/lib/properties/borderWidth.js @@ -17,12 +17,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -51,9 +52,13 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLength([value], { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + const parsedValue = parsers.parseLength([value], parseOpt); if (!parsedValue) { return; } @@ -111,7 +116,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/bottom.js b/lib/properties/bottom.js index 0e1902d1..dc1fd22d 100644 --- a/lib/properties/bottom.js +++ b/lib/properties/bottom.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "bottom"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/clear.js b/lib/properties/clear.js index cfd39c36..aa7eb085 100644 --- a/lib/properties/clear.js +++ b/lib/properties/clear.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "clear"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -34,7 +35,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/clip.js b/lib/properties/clip.js index 09908c0a..afcb59f2 100644 --- a/lib/properties/clip.js +++ b/lib/properties/clip.js @@ -7,12 +7,13 @@ const parsers = require("../parsers"); const property = "clip"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -24,8 +25,15 @@ module.exports.parse = function parse(v, opt = {}) { }); const parsedValues = []; for (const item of values) { - const parsedValue = parsers.parseCSS(item, { context: "value" }, true); - const val = parsers.parseLengthPercentage(parsedValue.children); + const parsedValue = parsers.parseCSS( + item, + { + globalObject, + options: { context: "value" } + }, + true + ); + const val = parsers.parseLengthPercentage(parsedValue.children, options); if (val) { parsedValues.push(val); } else { @@ -52,7 +60,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/color.js b/lib/properties/color.js index 74e398d0..9b48b9f9 100644 --- a/lib/properties/color.js +++ b/lib/properties/color.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/display.js b/lib/properties/display.js index fb45c479..de06987f 100644 --- a/lib/properties/display.js +++ b/lib/properties/display.js @@ -9,12 +9,13 @@ const displayOutside = ["block", "inline", "run-in"]; const displayFlow = ["flow", "flow-root"]; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -191,7 +192,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/flex.js b/lib/properties/flex.js index a319060e..dd484455 100644 --- a/lib/properties/flex.js +++ b/lib/properties/flex.js @@ -20,12 +20,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -115,6 +116,10 @@ module.exports.parse = function parse(v, opt = {}) { flex["flex-basis"] = `${val2.value}${val2.unit}`; break; } + case "Identifier": { + flex["flex-basis"] = val2.name; + break; + } case "Number": { flex["flex-shrink"] = val2.value; break; @@ -123,17 +128,12 @@ module.exports.parse = function parse(v, opt = {}) { flex["flex-basis"] = `${val2.value}%`; break; } - case "Identifier": { - flex["flex-basis"] = val2.name; - break; - } default: { return; } } } return flex; - // return [...Object.values(flex)].join(" "); } } else if (typeof value === "string") { return value; @@ -150,7 +150,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); const priority = this._priorities.get(property) ?? ""; if (typeof val === "string") { diff --git a/lib/properties/flexBasis.js b/lib/properties/flexBasis.js index 194ba579..b0bbd2dd 100644 --- a/lib/properties/flexBasis.js +++ b/lib/properties/flexBasis.js @@ -6,12 +6,13 @@ const property = "flex-basis"; const shorthand = "flex"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -28,7 +29,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -44,7 +45,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/flexGrow.js b/lib/properties/flexGrow.js index 89537d09..e40d7b89 100644 --- a/lib/properties/flexGrow.js +++ b/lib/properties/flexGrow.js @@ -6,12 +6,13 @@ const property = "flex-grow"; const shorthand = "flex"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue("flex-grow", v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseNumber(value); + return parsers.parseNumber(value, options); } } } else if (typeof value === "string") { @@ -43,7 +44,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/flexShrink.js b/lib/properties/flexShrink.js index 7b205a4b..c4927189 100644 --- a/lib/properties/flexShrink.js +++ b/lib/properties/flexShrink.js @@ -6,12 +6,13 @@ const property = "flex-shrink"; const shorthand = "flex"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseNumber(value); + return parsers.parseNumber(value, options); } } } else if (typeof value === "string") { @@ -43,7 +44,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/float.js b/lib/properties/float.js index 3e256318..6ffb6215 100644 --- a/lib/properties/float.js +++ b/lib/properties/float.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "float"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -34,7 +35,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/floodColor.js b/lib/properties/floodColor.js index 280bb112..7d1ded01 100644 --- a/lib/properties/floodColor.js +++ b/lib/properties/floodColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "flood-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/font.js b/lib/properties/font.js index 4cbcf553..804832f1 100644 --- a/lib/properties/font.js +++ b/lib/properties/font.js @@ -20,13 +20,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; + const { globalObject, options } = opt; if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { - v = parsers.resolveCalc(v); + v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v)) { + if (!parsers.isValidPropertyValue(property, v, globalObject)) { return; } const [fontBlock, ...families] = parsers.splitValue(v, { @@ -47,13 +47,15 @@ module.exports.parse = function parse(v, opt = {}) { return; } const lineHeightB = lineHeight.parse(lineB, { - global + globalObject, + options }); if (typeof lineHeightB !== "string") { return; } const familyB = fontFamily.parse(familiesB.join(" "), { globalObject, + options, caseSensitive: true }); if (typeof familyB === "string") { @@ -71,7 +73,8 @@ module.exports.parse = function parse(v, opt = {}) { switch (longhand) { case "font-size": { const parsedValue = fontSize.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { font[longhand] = parsedValue; @@ -83,7 +86,8 @@ module.exports.parse = function parse(v, opt = {}) { if (font[longhand] === "normal") { const longhandItem = module.exports.shorthandFor.get(longhand); const parsedValue = longhandItem.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { font[longhand] = parsedValue; @@ -94,7 +98,8 @@ module.exports.parse = function parse(v, opt = {}) { case "font-variant": { if (font[longhand] === "normal") { const parsedValue = fontVariant.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { if (parsedValue === "small-cap") { @@ -122,6 +127,7 @@ module.exports.parse = function parse(v, opt = {}) { const [part] = revParts; const value = parsers.parsePropertyValue(property, part, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -158,7 +164,8 @@ module.exports.parse = function parse(v, opt = {}) { if (font[longhand] === "normal") { const longhandItem = module.exports.shorthandFor.get(longhand); const parsedValue = longhandItem.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { font[longhand] = parsedValue; @@ -169,7 +176,8 @@ module.exports.parse = function parse(v, opt = {}) { case "font-variant": { if (font[longhand] === "normal") { const parsedValue = fontVariant.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { if (parsedValue === "small-cap") { @@ -187,13 +195,15 @@ module.exports.parse = function parse(v, opt = {}) { } } else { const parsedFontSize = fontSize.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedFontSize === "string") { fontSizeA = parsedFontSize; } else { const parsedFontFamily = fontFamily.parse(part, { globalObject, + options, caseSensitive: true }); if (typeof parsedFontFamily === "string") { @@ -206,6 +216,7 @@ module.exports.parse = function parse(v, opt = {}) { } const family = fontFamily.parse(revFontFamily.toReversed().join(" "), { globalObject, + options, caseSensitive: true }); if (fontSizeA && family) { @@ -218,6 +229,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const family of families) { const parsedFontFamily = fontFamily.parse(family, { globalObject, + options, caseSensitive: true }); if (parsedFontFamily) { @@ -240,7 +252,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const obj = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (!obj) { return; diff --git a/lib/properties/fontFamily.js b/lib/properties/fontFamily.js index db57001d..402cefae 100644 --- a/lib/properties/fontFamily.js +++ b/lib/properties/fontFamily.js @@ -6,10 +6,10 @@ const property = "font-family"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); @@ -17,6 +17,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, caseSensitive: true, inArray: true }); @@ -76,7 +77,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontSize.js b/lib/properties/fontSize.js index cdffa8e7..bcef3e6a 100644 --- a/lib/properties/fontSize.js +++ b/lib/properties/fontSize.js @@ -6,12 +6,13 @@ const property = "font-size"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -28,9 +29,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -46,7 +51,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontStyle.js b/lib/properties/fontStyle.js index b296798d..640afe90 100644 --- a/lib/properties/fontStyle.js +++ b/lib/properties/fontStyle.js @@ -6,12 +6,13 @@ const property = "font-style"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { } else if (value.length === 2) { const [part1, part2] = value; const val1 = part1.type === "Identifier" && part1.name; - const val2 = parsers.parseAngle([part2]); + const val2 = parsers.parseAngle([part2], options); if (val1 && val1 === "oblique" && val2) { return `${val1} ${val2}`; } @@ -45,7 +46,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontVariant.js b/lib/properties/fontVariant.js index b0d7749d..4e230fa7 100644 --- a/lib/properties/fontVariant.js +++ b/lib/properties/fontVariant.js @@ -6,15 +6,16 @@ const property = "font-variant"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = []; for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -55,7 +56,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontWeight.js b/lib/properties/fontWeight.js index d115a9dd..0bb826b4 100644 --- a/lib/properties/fontWeight.js +++ b/lib/properties/fontWeight.js @@ -6,12 +6,13 @@ const property = "font-weight"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -28,10 +29,14 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - const parsedValue = parsers.parseNumber(value, { + const parseOpt = { min: 1, max: 1000 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + const parsedValue = parsers.parseNumber(value, parseOpt); if (parsedValue) { return parsedValue; } @@ -50,7 +55,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/height.js b/lib/properties/height.js index 977d05f7..100e5ba6 100644 --- a/lib/properties/height.js +++ b/lib/properties/height.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "height"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/left.js b/lib/properties/left.js index 8b8b8d01..5217e26c 100644 --- a/lib/properties/left.js +++ b/lib/properties/left.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "left"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/lightingColor.js b/lib/properties/lightingColor.js index e1e321d4..21773f5b 100644 --- a/lib/properties/lightingColor.js +++ b/lib/properties/lightingColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "lighting-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/lineHeight.js b/lib/properties/lineHeight.js index f7eb7598..ee60c563 100644 --- a/lib/properties/lineHeight.js +++ b/lib/properties/lineHeight.js @@ -6,16 +6,23 @@ const property = "line-height"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { const [{ name, type, value: itemValue }] = value; + const parseOpt = { + min: 0 + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } switch (type) { case "Calc": { return `${name}(${itemValue})`; @@ -25,14 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { return name; } case "Number": { - return parsers.parseNumber(value, { - min: 0 - }); + return parsers.parseNumber(value, parseOpt); } default: { - return parsers.parseLengthPercentage(value, { - min: 0 - }); + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -48,7 +51,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/margin.js b/lib/properties/margin.js index df68cc04..ca3c6f9b 100644 --- a/lib/properties/margin.js +++ b/lib/properties/margin.js @@ -18,12 +18,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -53,7 +54,7 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLengthPercentage([value]); + const parsedValue = parsers.parseLengthPercentage([value], options); if (!parsedValue) { return; } @@ -79,7 +80,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/marginBottom.js b/lib/properties/marginBottom.js index 26aa15c0..f4cd4ddf 100644 --- a/lib/properties/marginBottom.js +++ b/lib/properties/marginBottom.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "bottom"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/marginLeft.js b/lib/properties/marginLeft.js index ae7ad63f..c38ed941 100644 --- a/lib/properties/marginLeft.js +++ b/lib/properties/marginLeft.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "left"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/marginRight.js b/lib/properties/marginRight.js index 41aca507..2e4a2174 100644 --- a/lib/properties/marginRight.js +++ b/lib/properties/marginRight.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "right"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/marginTop.js b/lib/properties/marginTop.js index e33edf71..b16a9bee 100644 --- a/lib/properties/marginTop.js +++ b/lib/properties/marginTop.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "top"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/opacity.js b/lib/properties/opacity.js index 467a349c..c252c05a 100644 --- a/lib/properties/opacity.js +++ b/lib/properties/opacity.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "opacity"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -24,10 +25,10 @@ module.exports.parse = function parse(v, opt = {}) { return name; } case "Number": { - return parsers.parseNumber(value); + return parsers.parseNumber(value, options); } case "Percentage": { - return parsers.parsePercentage(value); + return parsers.parsePercentage(value, options); } default: } @@ -43,7 +44,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/outlineColor.js b/lib/properties/outlineColor.js index be62e661..a6ed9680 100644 --- a/lib/properties/outlineColor.js +++ b/lib/properties/outlineColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "outline-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/padding.js b/lib/properties/padding.js index b574762f..c3d3f3ee 100644 --- a/lib/properties/padding.js +++ b/lib/properties/padding.js @@ -18,12 +18,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -49,9 +50,13 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLengthPercentage([value], { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + const parsedValue = parsers.parseLengthPercentage([value], parseOpt); if (!parsedValue) { return; } @@ -77,7 +82,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/paddingBottom.js b/lib/properties/paddingBottom.js index a2ec94da..89a6058e 100644 --- a/lib/properties/paddingBottom.js +++ b/lib/properties/paddingBottom.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "bottom"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/paddingLeft.js b/lib/properties/paddingLeft.js index fc67fd41..273fac3b 100644 --- a/lib/properties/paddingLeft.js +++ b/lib/properties/paddingLeft.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "left"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/paddingRight.js b/lib/properties/paddingRight.js index d6eb1ec7..f883f021 100644 --- a/lib/properties/paddingRight.js +++ b/lib/properties/paddingRight.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "right"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/paddingTop.js b/lib/properties/paddingTop.js index 59c84b86..7f44bf40 100644 --- a/lib/properties/paddingTop.js +++ b/lib/properties/paddingTop.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "top"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/right.js b/lib/properties/right.js index 46906797..38ece0fd 100644 --- a/lib/properties/right.js +++ b/lib/properties/right.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "right"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/stopColor.js b/lib/properties/stopColor.js index 65c605c4..b3fa2114 100644 --- a/lib/properties/stopColor.js +++ b/lib/properties/stopColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "stop-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/top.js b/lib/properties/top.js index 6da042ed..e8af5342 100644 --- a/lib/properties/top.js +++ b/lib/properties/top.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "top"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderAfterColor.js b/lib/properties/webkitBorderAfterColor.js index d9d7411d..ee0bc4fa 100644 --- a/lib/properties/webkitBorderAfterColor.js +++ b/lib/properties/webkitBorderAfterColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-after-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderBeforeColor.js b/lib/properties/webkitBorderBeforeColor.js index 6d61e5a7..6dc7f996 100644 --- a/lib/properties/webkitBorderBeforeColor.js +++ b/lib/properties/webkitBorderBeforeColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-before-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderEndColor.js b/lib/properties/webkitBorderEndColor.js index b8c6debf..fb58c297 100644 --- a/lib/properties/webkitBorderEndColor.js +++ b/lib/properties/webkitBorderEndColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-end-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderStartColor.js b/lib/properties/webkitBorderStartColor.js index e60d1df4..e75c4a72 100644 --- a/lib/properties/webkitBorderStartColor.js +++ b/lib/properties/webkitBorderStartColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-start-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitColumnRuleColor.js b/lib/properties/webkitColumnRuleColor.js index 0db57961..ec7e55da 100644 --- a/lib/properties/webkitColumnRuleColor.js +++ b/lib/properties/webkitColumnRuleColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-column-rule-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTapHighlightColor.js b/lib/properties/webkitTapHighlightColor.js index 06596563..f5f97d0a 100644 --- a/lib/properties/webkitTapHighlightColor.js +++ b/lib/properties/webkitTapHighlightColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-tap-highlight-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTextEmphasisColor.js b/lib/properties/webkitTextEmphasisColor.js index 84dac5ec..ab31c6ef 100644 --- a/lib/properties/webkitTextEmphasisColor.js +++ b/lib/properties/webkitTextEmphasisColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-text-emphasis-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTextFillColor.js b/lib/properties/webkitTextFillColor.js index f13dc649..dbb1aacd 100644 --- a/lib/properties/webkitTextFillColor.js +++ b/lib/properties/webkitTextFillColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-text-fill-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTextStrokeColor.js b/lib/properties/webkitTextStrokeColor.js index 2339702d..93241044 100644 --- a/lib/properties/webkitTextStrokeColor.js +++ b/lib/properties/webkitTextStrokeColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-text-stroke-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/width.js b/lib/properties/width.js index 6c60911f..6b212563 100644 --- a/lib/properties/width.js +++ b/lib/properties/width.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "width"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/utils/cache.js b/lib/utils/cache.js new file mode 100644 index 00000000..2669eb24 --- /dev/null +++ b/lib/utils/cache.js @@ -0,0 +1,34 @@ +"use strict"; + +const { LRUCache } = require("lru-cache"); + +// The lru-cache instance. +const lruCache = new LRUCache({ + max: 4096 +}); + +// The lru-cache requires non-null values, so substitute in this sentinel when we are given one. +const nullSentinel = Symbol("null"); + +/** + * @param key - Cache key. + * @param value - Value to cache. + * @returns void. + */ +const setCache = (key, value) => { + lruCache.set(key, value === null ? nullSentinel : value); +}; + +/** + * @param key - Cache key. + * @returns Cached item or false otherwise. + */ +const getCache = (key) => { + const value = lruCache.get(key); + return value === nullSentinel ? null : value; +}; + +module.exports = { + getCache, + setCache +}; diff --git a/lib/utils/propertyDescriptors.js b/lib/utils/propertyDescriptors.js index b0053db8..12612586 100644 --- a/lib/utils/propertyDescriptors.js +++ b/lib/utils/propertyDescriptors.js @@ -5,10 +5,66 @@ const parsers = require("../parsers"); exports.getPropertyDescriptor = function getPropertyDescriptor(property) { return { set(v) { - v = parsers.parsePropertyValue(property, v, { - globalObject: this._global - }); - this._setProperty(property, v); + const globalObject = this._global; + v = parsers.prepareValue(v, globalObject); + if (v === "" || parsers.hasVarFunc(v)) { + this._setProperty(property, v); + } else { + const options = this._options; + const val = parsers.parsePropertyValue(property, v, { + globalObject, + options, + inArray: true + }); + let value; + if (Array.isArray(val) && val.length === 1) { + const [{ name, raw, type, value: itemValue }] = val; + switch (type) { + case "Angle": { + value = parsers.parseAngle(val, options); + break; + } + case "Calc": { + value = `${name}(${itemValue})`; + break; + } + case "Dimension": { + value = parsers.parseLength(val, options); + break; + } + case "GlobalKeyword": + case "Identifier": { + value = name; + break; + } + case "Number": { + value = parsers.parseNumber(val, options); + break; + } + case "Percentage": { + value = parsers.parsePercentage(val, options); + break; + } + case "String": { + value = parsers.parseString(val, options); + break; + } + case "Url": { + value = parsers.parseURL(val, options); + break; + } + default: { + value = raw; + } + } + } else if (typeof val === "string") { + value = val; + } + if (typeof value === "string") { + const priority = this._priorities.get(property) ?? ""; + this._setProperty(property, value, priority); + } + } }, get() { return this.getPropertyValue(property); diff --git a/package-lock.json b/package-lock.json index 7d6744d6..236f4742 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,22 +9,23 @@ "version": "5.3.1", "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^4.0.3", + "@asamuzakjp/css-color": "^4.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", - "css-tree": "^3.1.0" + "css-tree": "^3.1.0", + "lru-cache": "^11.2.2" }, "devDependencies": { - "@babel/generator": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.2", + "@babel/generator": "^7.28.3", + "@babel/parser": "^7.28.4", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^6.23.6", - "eslint": "^9.32.0", + "@webref/css": "^7.1.1", + "eslint": "^9.37.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-prettier": "^5.5.3", - "globals": "^16.3.0", - "npm-run-all": "^4.1.5", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", + "npm-run-all2": "^8.0.4", "prettier": "^3.6.2", "resolve": "^1.22.10" }, @@ -33,16 +34,16 @@ } }, "node_modules/@asamuzakjp/css-color": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.3.tgz", - "integrity": "sha512-aixYrB8ESx3o1DnhOQsvjg5OCX0eK2pu4IjEKLbPhJofpREoUZDRp9orC6uUDih0V86eX8FINeU65LwiLOk32g==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz", + "integrity": "sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==", "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.4", - "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", - "lru-cache": "^11.1.0" + "lru-cache": "^11.2.1" } }, "node_modules/@babel/code-frame": { @@ -61,14 +62,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -108,13 +109,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -139,18 +140,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.4", "debug": "^4.3.1" }, "engines": { @@ -158,9 +159,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -172,9 +173,9 @@ } }, "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", "funding": [ { "type": "github", @@ -214,9 +215,9 @@ } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", "funding": [ { "type": "github", @@ -229,7 +230,7 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^5.0.2", + "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" }, "engines": { @@ -314,9 +315,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -371,19 +372,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -431,9 +435,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", "dev": true, "license": "MIT", "engines": { @@ -454,13 +458,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { @@ -600,9 +604,9 @@ "license": "MIT" }, "node_modules/@webref/css": { - "version": "6.23.6", - "resolved": "https://registry.npmjs.org/@webref/css/-/css-6.23.6.tgz", - "integrity": "sha512-PdgFNBJxoYc5WTJ5yxzpoxU6drWdtiXheaYmDh1YNiQc6sFxTJbUy5bfjHxXxQ37hhsLfWxuD3DG9L4GpRahoA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@webref/css/-/css-7.1.1.tgz", + "integrity": "sha512-TSG7svX3E0zSiiSsOuSFYt1UVQxLjRYNaWX7l1ER2QQ6EwEs3nbC5ruFdQXNtPEX3Y1HLUHSZFYnUsQhh+6rSA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -672,71 +676,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -755,56 +694,6 @@ "concat-map": "0.0.1" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -887,60 +776,6 @@ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -966,200 +801,6 @@ "dev": true, "license": "MIT" }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1174,20 +815,20 @@ } }, "node_modules/eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -1251,9 +892,9 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", - "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz", + "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==", "dev": true, "license": "MIT", "dependencies": { @@ -1454,22 +1095,6 @@ "dev": true, "license": "ISC" }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1480,238 +1105,42 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.13.0" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1725,13 +1154,6 @@ "node": ">= 0.4" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1769,112 +1191,6 @@ "node": ">=0.8.19" } }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -1891,41 +1207,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1936,41 +1217,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1984,188 +1230,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2213,12 +1277,15 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -2258,22 +1325,6 @@ "node": ">= 0.8.0" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2298,24 +1349,14 @@ "license": "MIT" }, "node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", "license": "ISC", "engines": { "node": "20 || >=22" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/mdn-data": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", @@ -2377,235 +1418,80 @@ "dev": true, "license": "MIT" }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/npm-run-all2": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-8.0.4.tgz", + "integrity": "sha512-wdbB5My48XKp2ZfJUlhnLVihzeuA1hgBnqB2J9ahV77wLS+/YAJAlN8I+X3DIFIPZ3m5L7nplmlbhNiFDmXRDA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" + "picomatch": "^4.0.2", + "pidtree": "^0.6.0", + "read-package-json-fast": "^4.0.0", + "shell-quote": "^1.7.3", + "which": "^5.0.0" }, "bin": { "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", "run-p": "bin/run-p/index.js", "run-s": "bin/run-s/index.js" }, "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "node": "^20.5.0 || >=22.0.0", + "npm": ">= 10" } }, - "node_modules/npm-run-all/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/npm-run-all/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "node": ">=12" }, - "engines": { - "node": ">=4.8" - } - }, - "node_modules/npm-run-all/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/npm-run-all/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/npm-run-all/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/npm-run-all2/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, + "license": "ISC", "engines": { - "node": ">=4" + "node": ">=16" } }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/npm-run-all2/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "which": "bin/which" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/optionator": { @@ -2626,24 +1512,6 @@ "node": ">= 0.8.0" } }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2689,20 +1557,6 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2730,56 +1584,36 @@ "dev": true, "license": "MIT" }, - "node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", "dev": true, "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": ">= 0.4" + "node": ">=0.10" } }, "node_modules/postcss": { @@ -2790,384 +1624,103 @@ { "type": "opencollective", "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "peer": true, "dependencies": { - "shebang-regex": "^3.0.0" + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": ">=8" + "node": "^10 || ^12 || >=14" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.0.0" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/read-package-json-fast": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz", + "integrity": "sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" @@ -3176,122 +1729,45 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", - "dev": true, - "license": "CC0-1.0" - }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "dev": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, "engines": { "node": ">= 0.4" }, @@ -3299,14 +1775,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, "node_modules/strip-json-comments": { @@ -3377,103 +1852,6 @@ "node": ">= 0.8.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3484,17 +1862,6 @@ "punycode": "^2.1.0" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3511,95 +1878,6 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 105e239e..560b9a3a 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "keywords": [ "CSS", "CSSStyleDeclaration", + "CSSStyleProperties", "StyleSheet" ], "version": "5.3.1", @@ -35,37 +36,39 @@ "files": [ "lib/" ], - "main": "./lib/CSSStyleDeclaration.js", + "main": "./lib/index.js", "dependencies": { - "@asamuzakjp/css-color": "^4.0.3", + "@asamuzakjp/css-color": "^4.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", - "css-tree": "^3.1.0" + "css-tree": "^3.1.0", + "lru-cache": "^11.2.2" }, "devDependencies": { - "@babel/generator": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.2", + "@babel/generator": "^7.28.3", + "@babel/parser": "^7.28.4", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^6.23.6", - "eslint": "^9.32.0", + "@webref/css": "^7.1.1", + "eslint": "^9.37.0", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-prettier": "^5.5.3", - "globals": "^16.3.0", - "npm-run-all": "^4.1.5", + "eslint-plugin-prettier": "^5.5.4", + "globals": "^16.4.0", + "npm-run-all2": "^8.0.4", "prettier": "^3.6.2", "resolve": "^1.22.10" }, "scripts": { - "download": "node ./scripts/downloadLatestProperties.mjs", + "download": "node ./scripts/downloadLatestProperties.mjs && npm run generate", "generate": "run-p generate:*", - "generate:implemented_properties": "node ./scripts/generateImplementedProperties.mjs", "generate:properties": "node ./scripts/generateProperties.js", + "generate:propertyList": "node ./scripts/generatePropertyList.mjs", "lint": "npm run generate && eslint --max-warnings 0", "lint:fix": "eslint --fix --max-warnings 0", - "prepublishOnly": "npm run lint && npm run test", - "test": "npm run generate && node --test", - "test-ci": "npm run lint && npm run test" + "prepublishOnly": "npm run test-ci", + "test": "npm run generate && npm run test:unit", + "test:unit": "node --test", + "test-ci": "npm run lint && npm run test:unit" }, "license": "MIT", "engines": { diff --git a/lib/utils/camelize.js b/scripts/camelize.js similarity index 94% rename from lib/utils/camelize.js rename to scripts/camelize.js index 19aaf7de..7f3fcefe 100644 --- a/lib/utils/camelize.js +++ b/scripts/camelize.js @@ -1,6 +1,6 @@ "use strict"; -const { asciiLowercase } = require("./strings"); +const { asciiLowercase } = require("../lib/utils/strings"); // Utility to translate from `border-width` to `borderWidth`. // NOTE: For values prefixed with webkit, e.g. `-webkit-foo`, we need to provide diff --git a/scripts/downloadLatestProperties.mjs b/scripts/downloadLatestProperties.mjs index 28fa2edf..ad3beae3 100644 --- a/scripts/downloadLatestProperties.mjs +++ b/scripts/downloadLatestProperties.mjs @@ -6,11 +6,8 @@ import fs from "node:fs"; import path from "node:path"; -import { fileURLToPath } from "node:url"; const url = "https://www.w3.org/Style/CSS/all-properties.en.json"; -const outputFile = resolve("../lib/generated/allProperties.js"); -const unwantedStatuses = new Set(["NOTE", "ED"]); console.log("Downloading CSS properties..."); @@ -22,17 +19,17 @@ if (res.status !== 200) { const rawCSSProperties = await res.json(); const propertyNames = new Set(); +const unwantedStatuses = new Set(["NOTE", "ED"]); for (const cssProp of rawCSSProperties) { // Filter out properties from too-new statuses. // Also, '--*' needs additional logic to this module, so filter it out for now. if (unwantedStatuses.has(cssProp.status) || cssProp.property === "--*") { continue; } - propertyNames.add(cssProp.property); } -const propertyNamesJSON = JSON.stringify(Array.from(propertyNames), undefined, 2); +const propertyNamesJSON = JSON.stringify([...propertyNames], undefined, 2); const dateToday = new Date(); const [dateTodayFormatted] = dateToday.toISOString().split("T"); const output = `"use strict"; @@ -42,9 +39,6 @@ const output = `"use strict"; module.exports = new Set(${propertyNamesJSON}); `; +const { dirname } = import.meta; +const outputFile = path.resolve(dirname, "../lib/generated/allProperties.js"); fs.writeFileSync(outputFile, output); - -// TODO: remove when we can drop Node.js 18 support and use import.meta.dirname. -function resolve(relativePath) { - return path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath); -} diff --git a/scripts/generateImplementedProperties.mjs b/scripts/generateImplementedProperties.mjs deleted file mode 100644 index efa807a0..00000000 --- a/scripts/generateImplementedProperties.mjs +++ /dev/null @@ -1,42 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import css from "@webref/css"; -import { camelCaseToDashed } from "../lib/utils/camelize.js"; - -const parsedFiles = await css.listAll(); -const definitions = new Map(); -for (const { properties } of Object.values(parsedFiles)) { - if (Array.isArray(properties)) { - for (const definition of properties) { - const { name } = definition; - if (name) { - definitions.set(name, definition); - } - } - } -} - -const { dirname } = import.meta; -const dashedProperties = fs - .readdirSync(path.resolve(dirname, "../lib/properties")) - .filter((propertyFile) => path.extname(propertyFile) === ".js") - .map((propertyFile) => camelCaseToDashed(path.basename(propertyFile, ".js"))) - .toSorted(); - -const implementedProperties = new Map(); -for (const property of dashedProperties) { - const definition = definitions.get(property); - implementedProperties.set(property, definition); -} - -const outputFile = path.resolve(dirname, "../lib/generated/implementedProperties.js"); - -const dateToday = new Date(); -const [dateTodayFormatted] = dateToday.toISOString().split("T"); -const output = `"use strict"; -// autogenerated - ${dateTodayFormatted} - -module.exports = new Map(${JSON.stringify([...implementedProperties], null, 2)}); -`; - -fs.writeFileSync(outputFile, output); diff --git a/scripts/generateProperties.js b/scripts/generateProperties.js index 61aec085..30372f2d 100644 --- a/scripts/generateProperties.js +++ b/scripts/generateProperties.js @@ -7,7 +7,7 @@ const t = require("@babel/types"); const generate = require("@babel/generator").default; const traverse = require("@babel/traverse").default; const resolve = require("resolve"); -const { camelCaseToDashed } = require("../lib/utils/camelize"); +const { camelCaseToDashed } = require("./camelize"); const { basename, dirname } = nodePath; diff --git a/scripts/generatePropertyList.mjs b/scripts/generatePropertyList.mjs new file mode 100644 index 00000000..c5bfb02c --- /dev/null +++ b/scripts/generatePropertyList.mjs @@ -0,0 +1,47 @@ +import fs from "node:fs"; +import path from "node:path"; +import css from "@webref/css"; +import allProperties from "../lib/generated/allProperties.js"; +import allExtraProperties from "../lib/utils/allExtraProperties.js"; + +const unifiedProperties = + typeof allProperties.union === "function" + ? allProperties.union(allExtraProperties) + : new Set([...allProperties, ...allExtraProperties]); + +const { properties } = await css.listAll(); +const definitions = new Map(); +if (Array.isArray(properties)) { + for (const definition of properties) { + const { href, initial, inherited, computedValue, legacyAliasOf, name, styleDeclaration } = + definition; + if (unifiedProperties.has(name)) { + definitions.set(name, { + href, + initial, + inherited, + computedValue, + legacyAliasOf, + name, + styleDeclaration + }); + } + } +} + +const propertyList = new Map(); +for (const property of [...unifiedProperties].toSorted()) { + const definition = definitions.get(property); + if (definition) { + propertyList.set(property, definition); + } +} +const [dateTodayFormatted] = new Date().toISOString().split("T"); +const output = `"use strict"; +// autogenerated - ${dateTodayFormatted} + +module.exports = new Map(${JSON.stringify([...propertyList], null, 2)}); +`; +const { dirname } = import.meta; +const outputFile = path.resolve(dirname, "../lib/generated/propertyList.js"); +fs.writeFileSync(outputFile, output); diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 91ab46e3..d0c82680 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -2,50 +2,17 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); -const { CSSStyleDeclaration, propertyList } = require("../lib/CSSStyleDeclaration"); -const allProperties = require("../lib/generated/allProperties"); -const implementedProperties = require("../lib/generated/implementedProperties"); -const allExtraProperties = require("../lib/utils/allExtraProperties"); -const camelize = require("../lib/utils/camelize"); +const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); describe("CSSStyleDeclaration", () => { - const dashedProperties = [...allProperties, ...allExtraProperties]; - const allowedProperties = dashedProperties.map(camelize.dashedToCamelCase); - const invalidProperties = [...implementedProperties.keys()] - .map(camelize.dashedToCamelCase) - .filter((prop) => !allowedProperties.includes(prop)); - - it("has only valid properties implemented", () => { - assert.strictEqual(invalidProperties.length, 0); - }); - - it("does not enumerate constructor or internals", () => { - const style = new CSSStyleDeclaration(); - assert.strictEqual(Object.getOwnPropertyDescriptor(style, "constructor").enumerable, false); - for (const i in style) { - assert.strictEqual(i.startsWith("_"), false); - } - }); - - it("has all properties", () => { - const style = new CSSStyleDeclaration(); - allProperties.forEach((property) => { - assert.ok(style.__lookupGetter__(property)); - assert.ok(style.__lookupSetter__(property)); - }); - }); - - it("has dashed properties", () => { - const style = new CSSStyleDeclaration(); - dashedProperties.forEach((property) => { - assert.ok(style.__lookupGetter__(property)); - assert.ok(style.__lookupSetter__(property)); - }); - }); - - it("has all functions", () => { - const style = new CSSStyleDeclaration(); - + const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError + }; + + it("has methods", () => { + const style = new CSSStyleDeclaration(window); assert.strictEqual(typeof style.item, "function"); assert.strictEqual(typeof style.getPropertyValue, "function"); assert.strictEqual(typeof style.setProperty, "function"); @@ -53,34 +20,8 @@ describe("CSSStyleDeclaration", () => { assert.strictEqual(typeof style.removeProperty, "function"); }); - it("has PascalCase for webkit prefixed properties", () => { - const style = new CSSStyleDeclaration(); - for (const i in style) { - if (/^webkit[A-Z]/.test(i)) { - const pascal = i.replace(/^webkit/, "Webkit"); - assert.ok(style[pascal] !== undefined); - } - } - }); - - it("throws if argument is not given", () => { - const style = new CSSStyleDeclaration(); - - assert.throws( - () => { - style.item(); - }, - (e) => { - assert.strictEqual(e instanceof globalThis.TypeError, true); - assert.strictEqual(e.message, "1 argument required, but only 0 present."); - return true; - } - ); - }); - - it("has special properties", () => { - const style = new CSSStyleDeclaration(); - + it("has attributes", () => { + const style = new CSSStyleDeclaration(window); assert.ok(style.__lookupGetter__("cssText")); assert.ok(style.__lookupSetter__("cssText")); assert.ok(style.__lookupGetter__("length")); @@ -88,141 +29,24 @@ describe("CSSStyleDeclaration", () => { assert.ok(style.__lookupGetter__("parentRule")); }); - it("sets internals for Window", () => { - const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException - }; - const style = new CSSStyleDeclaration(null, { - context: window - }); - - assert.strictEqual(style.cssText, ""); - assert.throws( - () => { - style.cssText = "color: green;"; - }, - (e) => { - assert.strictEqual(e instanceof window.DOMException, true); - assert.strictEqual(e.name, "NoModificationAllowedError"); - assert.strictEqual(e.message, "cssText can not be modified."); - return true; - } - ); - assert.throws( - () => { - style.removeProperty("color"); - }, - (e) => { - assert.strictEqual(e instanceof window.DOMException, true); - assert.strictEqual(e.name, "NoModificationAllowedError"); - assert.strictEqual(e.message, "Property color can not be modified."); - return true; - } - ); - }); - it("sets internals for Element", () => { const node = { nodeType: 1, style: {}, ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } - } - }; - const style = new CSSStyleDeclaration(null, { - context: node - }); - style.cssText = "color: green"; - assert.strictEqual(style.cssText, "color: green;"); - }); - - it("sets empty string for invalid cssText", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } - } - }; - const style = new CSSStyleDeclaration(null, { - context: node - }); - style.cssText = "color: green!"; - assert.strictEqual(style.cssText, ""); - }); - - it("sets only the valid properties for partially valid cssText", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } + defaultView: window } }; - const style = new CSSStyleDeclaration(null, { - context: node - }); - - // valid property followed by invalid property followed by valid property - style.cssText = "color: green; color: invalid!; background: blue;"; - // ignores invalid properties - assert.strictEqual(style.cssText, "color: green; background: blue;"); - - // only valid properties - style.cssText = "color: olivedrab; color: peru; background: bisque;"; - // keeps the last one of the same property - assert.strictEqual(style.cssText, "color: peru; background: bisque;"); - - // valid property followed by a nested selector rule - style.cssText = "color: olivedrab; &.d { color: peru; }"; - // ignores the nested selector rule - assert.strictEqual(style.cssText, "color: olivedrab;"); - - // valid property followed by a nested selector rule followed by two valid properties and an invalid property - style.cssText = - "color: olivedrab; &.d { color: peru; } color: green; background: red; invalid: rule;"; - // ignores the property immediately after the nested rule - assert.strictEqual(style.cssText, "color: olivedrab; background: red;"); - - // valid property followed by a at-rule followed by a valid property - style.cssText = "color: blue; @media screen { color: red; } color: orange;"; - // includes the the property immediately after an at-rule - assert.strictEqual(style.cssText, "color: orange;"); - - // valid property followed by a nested rule, two at-rules and two valid properties - style.cssText = ` - color: blue; - &.d { color: peru; } - @media screen { color: red; } - @layer { color: black; } - color: pink; - background: orange;`; - // ignores the first property found after the nested selector rule along with the at-rules - assert.strictEqual(style.cssText, "color: blue; background: orange;"); - }); - - it("sets internals for Element", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } - } + let callCount = 0; + const callback = () => { + callCount++; }; - const style = new CSSStyleDeclaration(null, { - context: node + const style = new CSSStyleDeclaration(window, { + context: node, + onChange: callback }); - style.cssText = "color: light-dark(#008000, #0000ff)"; - assert.strictEqual(style.cssText, "color: light-dark(rgb(0, 128, 0), rgb(0, 0, 255));"); + style.cssText = "color: green;"; + assert.strictEqual(callCount, 1); }); it("sets internals for CSSRule", () => { @@ -231,1422 +55,149 @@ describe("CSSStyleDeclaration", () => { parentStyleSheet: { ownerDocument: { defaultView: { - DOMException: globalThis.DOMException + DOMException: window.DOMException } } } }; - const style = new CSSStyleDeclaration(null, { + const style = new CSSStyleDeclaration(window, { context: rule }); - style.cssText = "color: green"; - assert.strictEqual(style.cssText, ""); - }); - - it("from style string", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; - assert.strictEqual(style.length, 4); - assert.strictEqual( - style.cssText, - "color: blue; background-color: red; width: 78%; height: 50vh;" - ); - assert.strictEqual(style.getPropertyValue("color"), "blue"); - assert.strictEqual(style.item(0), "color"); - assert.strictEqual(style[1], "background-color"); - assert.strictEqual(style.backgroundColor, "red"); - style.cssText = ""; - assert.strictEqual(style.cssText, ""); - assert.strictEqual(style.length, 0); - }); - - it("from properties", () => { - const style = new CSSStyleDeclaration(); - style.color = "blue"; - assert.strictEqual(style.length, 1); - assert.strictEqual(style[0], "color"); - assert.strictEqual(style.cssText, "color: blue;"); - assert.strictEqual(style.item(0), "color"); - assert.strictEqual(style.color, "blue"); - style.backgroundColor = "red"; - assert.strictEqual(style.length, 2); - assert.strictEqual(style[0], "color"); - assert.strictEqual(style[1], "background-color"); - assert.strictEqual(style.cssText, "color: blue; background-color: red;"); - assert.strictEqual(style.backgroundColor, "red"); - style.removeProperty("color"); - assert.strictEqual(style[0], "background-color"); - }); - - it("shorthand properties", () => { - const style = new CSSStyleDeclaration(); - style.background = "blue url(http://www.example.com/some_img.jpg)"; - assert.strictEqual(style.backgroundColor, "blue"); - assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); - assert.strictEqual(style.background, 'url("http://www.example.com/some_img.jpg") blue'); - style.border = "0 solid black"; - assert.strictEqual(style.borderWidth, "0px"); - assert.strictEqual(style.borderStyle, "solid"); - assert.strictEqual(style.borderColor, "black"); - assert.strictEqual(style.borderTopWidth, "0px"); - assert.strictEqual(style.borderLeftStyle, "solid"); - assert.strictEqual(style.borderBottomColor, "black"); - style.font = "12em monospace"; - assert.strictEqual(style.fontSize, "12em"); - assert.strictEqual(style.fontFamily, "monospace"); - }); - - it("width and height properties and null and empty strings", () => { - const style = new CSSStyleDeclaration(); - style.height = 6; - assert.strictEqual(style.height, ""); - style.width = 0; - assert.strictEqual(style.width, "0px"); - style.height = "34%"; - assert.strictEqual(style.height, "34%"); - style.height = "100vh"; - assert.strictEqual(style.height, "100vh"); - style.height = "100vw"; - assert.strictEqual(style.height, "100vw"); - style.height = ""; - assert.strictEqual(style.length, 1); - assert.strictEqual(style.cssText, "width: 0px;"); - style.width = null; - assert.strictEqual(style.length, 0); - assert.strictEqual(style.cssText, ""); - }); - - it("implicit properties", () => { - const style = new CSSStyleDeclaration(); - style.borderWidth = 0; - assert.strictEqual(style.border, ""); - assert.strictEqual(style.borderWidth, "0px"); - assert.strictEqual(style.borderTopWidth, "0px"); - assert.strictEqual(style.borderBottomWidth, "0px"); - assert.strictEqual(style.borderLeftWidth, "0px"); - assert.strictEqual(style.borderRightWidth, "0px"); - assert.strictEqual(style.cssText, "border-width: 0px;"); - }); - - it("top, left, right, bottom properties", () => { - const style = new CSSStyleDeclaration(); - style.top = 0; - style.left = "0%"; - style.right = "5em"; - style.bottom = "12pt"; - assert.strictEqual(style.top, "0px"); - assert.strictEqual(style.left, "0%"); - assert.strictEqual(style.right, "5em"); - assert.strictEqual(style.bottom, "12pt"); - assert.strictEqual(style.length, 4); - assert.strictEqual(style.cssText, "top: 0px; left: 0%; right: 5em; bottom: 12pt;"); - }); - - it('top, left, right, bottom properties should accept "auto"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; - assert.strictEqual(style.top, "auto"); - assert.strictEqual(style.right, "auto"); - assert.strictEqual(style.bottom, "auto"); - assert.strictEqual(style.left, "auto"); - }); - - it("clear and clip properties", () => { - const style = new CSSStyleDeclaration(); - style.clear = "none"; - assert.strictEqual(style.clear, "none"); - style.clear = "lfet"; - assert.strictEqual(style.clear, "none"); - style.clear = "left"; - assert.strictEqual(style.clear, "left"); - style.clear = "right"; - assert.strictEqual(style.clear, "right"); - style.clear = "both"; - assert.strictEqual(style.clear, "both"); - style.clip = "elipse(5px, 10px)"; - assert.strictEqual(style.clip, ""); - assert.strictEqual(style.length, 1); - style.clip = "rect(0, 3Em, 2pt, 50%)"; - assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 50%)"); - assert.strictEqual(style.length, 2); - assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); - }); - - it("colors", () => { - const style = new CSSStyleDeclaration(); - style.color = "rgba(0,0,0,0)"; - assert.strictEqual(style.color, "rgba(0, 0, 0, 0)"); - style.color = "rgba(5%, 10%, 20%, 0.4)"; - assert.strictEqual(style.color, "rgba(13, 26, 51, 0.4)"); - style.color = "rgb(33%, 34%, 33%)"; - assert.strictEqual(style.color, "rgb(84, 87, 84)"); - style.color = "rgba(300, 200, 100, 1.5)"; - assert.strictEqual(style.color, "rgb(255, 200, 100)"); - style.color = "hsla(0, 1%, 2%, 0.5)"; - assert.strictEqual(style.color, "rgba(5, 5, 5, 0.5)"); - style.color = "hsl(0, 1%, 2%)"; - assert.strictEqual(style.color, "rgb(5, 5, 5)"); - style.color = "rebeccapurple"; - assert.strictEqual(style.color, "rebeccapurple"); - style.color = "transparent"; - assert.strictEqual(style.color, "transparent"); - style.color = "currentcolor"; - assert.strictEqual(style.color, "currentcolor"); - style.color = "#ffffffff"; - assert.strictEqual(style.color, "rgb(255, 255, 255)"); - style.color = "#fffa"; - assert.strictEqual(style.color, "rgba(255, 255, 255, 0.667)"); - style.color = "#ffffff66"; - assert.strictEqual(style.color, "rgba(255, 255, 255, 0.4)"); - }); - - it("invalid hex color value", () => { - const style = new CSSStyleDeclaration(); - style.color = "#1234567"; - assert.strictEqual(style.color, ""); - }); - - it("shorthand properties with embedded spaces", () => { - let style = new CSSStyleDeclaration(); - style.background = "rgb(0, 0, 0) url(/something/somewhere.jpg)"; - assert.strictEqual(style.backgroundColor, "rgb(0, 0, 0)"); - assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")'); - assert.strictEqual(style.cssText, 'background: url("/something/somewhere.jpg") rgb(0, 0, 0);'); - style = new CSSStyleDeclaration(); - style.border = " 1px solid black "; - assert.strictEqual(style.border, "1px solid black"); - }); - - it("setting shorthand properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.borderWidth = "1px"; - assert.strictEqual(style.cssText, "border-width: 1px;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("setting implicit properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderWidth = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.borderTopWidth = "2px"; - assert.strictEqual(style.cssText, "border-top-width: 2px;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - style.borderTop = "2px solid black"; - assert.strictEqual(style.cssText, "border-top: 2px solid black;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("set border as none", () => { - const style = new CSSStyleDeclaration(); - style.border = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border as none", () => { - const style = new CSSStyleDeclaration(); - style.border = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border-style as none", () => { - const style = new CSSStyleDeclaration(); - style.borderStyle = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual(style.cssText, "border-style: none;", "cssText"); - }); - - it("set border-top as none", () => { - const style = new CSSStyleDeclaration(); - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual(style.cssText, "border-top: medium;", "cssText"); - }); - - it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "1px", "border"); - assert.strictEqual(style.borderWidth, "1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "1px", "border-top"); - assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: 1px;", "cssText"); - }); - - it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "1px", "border"); - assert.strictEqual(style.borderWidth, "1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "1px", "border-top"); - assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: 1px;", "cssText"); - }); - - it("set border as 1px and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as 1px solid and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none solid solid; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as none and change border-style to null", () => { - const style = new CSSStyleDeclaration(); - style.border = "none"; - style.borderStyle = null; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as solid and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium; border-style: none solid solid; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as solid and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "solid"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border-style as solid and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.borderStyle = "solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual( - style.cssText, - "border-style: none solid solid; border-top-width: medium; border-top-color: currentcolor;", - "cssText" - ); - }); - - it("set border-top as solid and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.borderTop = "solid"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual( - style.cssText, - "border-top-width: medium; border-top-color: currentcolor; border-style: none;", - "cssText" - ); - }); - - it("set border-style as solid and change border-top to null", () => { - const style = new CSSStyleDeclaration(); - style.borderStyle = "solid"; - style.borderTop = null; - assert.strictEqual( - style.cssText, - "border-right-style: solid; border-bottom-style: solid; border-left-style: solid;", - "cssText" - ); - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - }); - - it("setting border values to none should change dependent values", () => { - const style = new CSSStyleDeclaration(); - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.border = "none"; - assert.strictEqual(style.border, "medium"); - assert.strictEqual(style.borderTop, "medium"); - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "medium"); - assert.strictEqual(style.cssText, "border: medium;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderStyle = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - assert.strictEqual(style.cssText, "border-top-width: 1px; border-style: none;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderTop = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "medium"); - assert.strictEqual(style.cssText, "border-top: medium;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderTopStyle = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - assert.strictEqual(style.cssText, "border-top-width: 1px; border-top-style: none;"); - - style.border = null; - style.borderImage = null; - style.border = "1px"; - assert.strictEqual(style.cssText, "border: 1px;"); - assert.strictEqual(style.border, "1px"); - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - style.borderTop = "none"; - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;" - ); - }); - - it("setting border to green", () => { - const style = new CSSStyleDeclaration(); - style.border = "green"; - assert.strictEqual(style.cssText, "border: green;"); - assert.strictEqual(style.border, "green"); - }); - - it("setting border to green", () => { - const style = new CSSStyleDeclaration(); - style.border = "green"; - assert.strictEqual(style.cssText, "border: green;"); - assert.strictEqual(style.border, "green"); - }); - - it("setting border to initial should set all properties initial", () => { - const style = new CSSStyleDeclaration(); - style.border = "initial"; - assert.strictEqual(style.cssText, "border: initial;"); - assert.strictEqual(style.border, "initial"); - assert.strictEqual(style.borderWidth, "initial"); - assert.strictEqual(style.borderStyle, "initial"); - assert.strictEqual(style.borderColor, "initial"); - assert.strictEqual(style.borderTop, "initial"); - assert.strictEqual(style.borderTopWidth, "initial"); - assert.strictEqual(style.borderTopStyle, "initial"); - assert.strictEqual(style.borderTopColor, "initial"); - assert.strictEqual(style.borderImage, "none"); - }); - - it("setting borderTop to initial should set top related properties initial", () => { - const style = new CSSStyleDeclaration(); - style.borderTop = "initial"; - assert.strictEqual(style.cssText, "border-top: initial;"); - assert.strictEqual(style.border, ""); - assert.strictEqual(style.borderWidth, ""); - assert.strictEqual(style.borderStyle, ""); - assert.strictEqual(style.borderColor, ""); - assert.strictEqual(style.borderTop, "initial"); - assert.strictEqual(style.borderTopWidth, "initial"); - assert.strictEqual(style.borderTopStyle, "initial"); - assert.strictEqual(style.borderTopColor, "initial"); - assert.strictEqual(style.borderImage, ""); - }); - - it("setting border to 0 should be okay", () => { - const style = new CSSStyleDeclaration(); - style.border = 0; - assert.strictEqual(style.cssText, "border: 0px;"); - assert.strictEqual(style.border, "0px"); - }); - - it("setting borderColor to var() should be okay", () => { - const style = new CSSStyleDeclaration(); - style.borderColor = "var(--foo)"; - assert.strictEqual(style.cssText, "border-color: var(--foo);"); - }); - - it("setting borderColor to inherit should be okay", () => { - const style = new CSSStyleDeclaration(); - style.borderColor = "inherit"; - assert.strictEqual(style.cssText, "border-color: inherit;"); - }); - - it("setting values implicit and shorthand properties via csstext and setproperty should propagate to dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border: 1px solid black;"; - assert.strictEqual(style.cssText, "border: 1px solid black;"); - assert.strictEqual(style.borderTop, "1px solid black"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - style.setProperty("border", "1px solid black"); - assert.strictEqual(style.cssText, "border: 1px solid black;"); - }); - - it("setting opacity should work", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("opacity", 0.75); - assert.strictEqual(style.cssText, "opacity: 0.75;"); - style.opacity = "0.50"; - assert.strictEqual(style.cssText, "opacity: 0.5;"); - style.opacity = 1; - assert.strictEqual(style.cssText, "opacity: 1;"); - }); - - it("width and height of auto should work", () => { - let style = new CSSStyleDeclaration(); - style.width = "auto"; - assert.strictEqual(style.cssText, "width: auto;"); - assert.strictEqual(style.width, "auto"); - style = new CSSStyleDeclaration(); - style.height = "auto"; - assert.strictEqual(style.cssText, "height: auto;"); - assert.strictEqual(style.height, "auto"); - }); - - it("Shorthand serialization with just longhands", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;"; - assert.strictEqual(style.cssText, "margin: 10px;"); - assert.strictEqual(style.margin, "10px"); - - style.cssText = - "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;"; - assert.strictEqual( - style.cssText, - "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;" - ); - assert.strictEqual(style.margin, ""); - - style.cssText = - "margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;"; - assert.strictEqual(style.cssText, "margin: 10px !important;"); - assert.strictEqual(style.margin, "10px"); + assert.deepEqual(style.parentRule, rule); }); - it("padding and margin should set/clear shorthand properties", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - const testParts = function (name, v, V) { - style[name] = v; - for (let i = 0; i < 4; i++) { - const part = name + parts[i]; - assert.strictEqual(style[part], V[i]); - } - - assert.strictEqual(style[name], v); - style[name] = ""; - }; - testParts("padding", "1px", ["1px", "1px", "1px", "1px"]); - testParts("padding", "1px 2%", ["1px", "2%", "1px", "2%"]); - testParts("padding", "1px 2px 3px", ["1px", "2px", "3px", "2px"]); - testParts("padding", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); - style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = "1px"; - testParts("padding", "", ["", "", "", ""]); - testParts("margin", "1px", ["1px", "1px", "1px", "1px"]); - testParts("margin", "1px auto", ["1px", "auto", "1px", "auto"]); - testParts("margin", "1px 2% 3px", ["1px", "2%", "3px", "2%"]); - testParts("margin", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); - style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = "1px"; - testParts("margin", "", ["", "", "", ""]); - }); - - it("padding and margin shorthands should set main properties", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - const testParts = function (name, v, V) { - let expected; - for (let i = 0; i < 4; i++) { - style[name] = v; - style[name + parts[i]] = V; - expected = v.split(/ /); - expected[i] = V; - expected = expected.join(" "); - - assert.strictEqual(style[name], expected); - } - }; - testParts("padding", "1px 2px 3px 4px", "10px"); - testParts("margin", "1px 2px 3px 4px", "10px"); - testParts("margin", "1px 2px 3px 4px", "auto"); - }); - - it("setting individual padding and margin properties to an empty string should clear them", () => { - const style = new CSSStyleDeclaration(); - - const properties = ["padding", "margin"]; - const parts = ["Top", "Right", "Bottom", "Left"]; - for (let i = 0; i < properties.length; i++) { - for (let j = 0; j < parts.length; j++) { - const property = properties[i] + parts[j]; - style[property] = "12px"; - assert.strictEqual(style[property], "12px"); - - style[property] = ""; - assert.strictEqual(style[property], ""); - } - } - }); - - it("removing and setting individual margin properties updates the combined property accordingly", () => { - const style = new CSSStyleDeclaration(); - style.margin = "1px 2px 3px 4px"; - style.marginTop = ""; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginBottom, "3px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginBottom = ""; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginBottom = "5px"; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginBottom, "5px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginTop = "6px"; - assert.strictEqual(style.cssText, "margin: 6px 2px 5px 4px;"); - }); - - for (const property of ["padding", "margin"]) { - it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - const partValues = ["1px", "2px", "3px", "4px"]; - - for (let j = 0; j < parts.length; j++) { - const partToRemove = parts[j]; - style[property] = partValues.join(" "); - style[property + partToRemove] = ""; - - // Main property should have been removed - assert.strictEqual(style[property], ""); - - // Expect other parts to still be there - for (let k = 0; k < parts.length; k++) { - const propertyCss = `${property}-${parts[k].toLowerCase()}: ${partValues[k]};`; - if (k === j) { - assert.strictEqual(style[property + parts[k]], ""); - assert.strictEqual(style.cssText.includes(propertyCss), false); - } else { - assert.strictEqual(style[property + parts[k]], partValues[k]); - assert.strictEqual(style.cssText.includes(propertyCss), true); - } - } - } - }); - - it(`setting additional ${property} properties keeps important status of others`, () => { - const style = new CSSStyleDeclaration(); - const importantProperty = `${property}-top: 3px !important;`; - style.cssText = importantProperty; - assert.strictEqual(style.cssText.includes(importantProperty), true); - - style[`${property}Right`] = "4px"; - style[`${property}Bottom`] = "5px"; - style[`${property}Left`] = "6px"; - assert.strictEqual(style.cssText.includes(importantProperty), true); - assert.strictEqual(style.cssText.includes(`${property}-right: 4px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-bottom: 5px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-left: 6px;`), true); - assert.strictEqual(style.cssText.includes("margin:"), false); - }); - - it(`setting individual ${property} keeps important status of others`, () => { - const style = new CSSStyleDeclaration(); - style.cssText = `${property}: 3px !important;`; - style[`${property}Top`] = "4px"; - assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); - assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); - assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); - assert.strictEqual(style.cssText.includes("margin:"), false); + it("has format in internal options", () => { + const style = new CSSStyleDeclaration(window, { + foo: "bar" }); - } - - it("setting a value to 0 should return the string value", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("fill-opacity", 0); - assert.strictEqual(style.fillOpacity, "0"); - }); - - it("onchange callback should be called when the csstext changes", () => { - let called = 0; - const style = new CSSStyleDeclaration(function (cssText) { - called++; - assert.strictEqual(cssText, "opacity: 0;"); + assert.deepEqual(style._options, { + format: "specifiedValue" }); - style.cssText = "opacity: 0;"; - assert.strictEqual(called, 1); - style.cssText = "opacity: 0;"; - assert.strictEqual(called, 2); }); - it("onchange callback should be called only once when multiple properties were added", () => { - let called = 0; - const style = new CSSStyleDeclaration(function (cssText) { - called++; - assert.strictEqual(cssText, "width: 100px; height: 100px;"); + it("should not override format if exists", () => { + const style = new CSSStyleDeclaration(window, { + format: "computedValue" }); - style.cssText = "width: 100px;height:100px;"; - assert.strictEqual(called, 1); - }); - - it("onchange callback should not be called when property is set to the same value", () => { - let called = 0; - const style = new CSSStyleDeclaration(function () { - called++; + assert.deepEqual(style._options, { + format: "computedValue" }); - - style.setProperty("opacity", 0); - assert.strictEqual(called, 1); - style.setProperty("opacity", 0); - assert.strictEqual(called, 1); }); - it("onchange callback should not be called when removeProperty was called on non-existing property", () => { - let called = 0; - const style = new CSSStyleDeclaration(function () { - called++; + it("getting cssText returns empty string if computed flag is set", () => { + const style = new CSSStyleDeclaration(window, { + format: "computedValue" }); - style.removeProperty("opacity"); - assert.strictEqual(called, 0); - }); - - it("setting float should work the same as cssfloat", () => { - const style = new CSSStyleDeclaration(); - style.float = "left"; - assert.strictEqual(style.cssFloat, "left"); + style.cssText = "color: red;"; + assert.strictEqual(style.cssText, ""); }); - it("setting improper css to csstext should not throw", () => { - const style = new CSSStyleDeclaration(); + it("setting improper css to cssText should not throw", () => { + const style = new CSSStyleDeclaration(window); style.cssText = "color: "; assert.strictEqual(style.cssText, ""); - style.color = "black"; - style.cssText = "float: "; + style.cssText = "color: red!"; assert.strictEqual(style.cssText, ""); }); - it("url parsing works with quotes", () => { - const style = new CSSStyleDeclaration(); - style.backgroundImage = "url(http://some/url/here1.png)"; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); - style.backgroundImage = "url('http://some/url/here2.png')"; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); - style.backgroundImage = 'url("http://some/url/here3.png")'; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); - }); - - it("setting 0 to a padding or margin works", () => { - const style = new CSSStyleDeclaration(); - style.padding = 0; - assert.strictEqual(style.cssText, "padding: 0px;"); - style.margin = "1em"; - style.marginTop = "0"; - assert.strictEqual(style.marginTop, "0px"); - }); - - it("setting ex units to a padding or margin works", () => { - const style = new CSSStyleDeclaration(); - style.padding = "1ex"; - assert.strictEqual(style.cssText, "padding: 1ex;"); - style.margin = "1em"; - style.marginTop = "0.5ex"; - assert.strictEqual(style.marginTop, "0.5ex"); - }); - - it("setting empty string and null to a padding or margin works", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - function testParts(base, nullValue) { - const props = [base].concat(parts.map((part) => base + part)); - for (const prop of props) { - assert.strictEqual(style[prop], ""); - style[prop] = "10px"; - assert.strictEqual(style[prop], "10px"); - style[prop] = nullValue; - assert.strictEqual(style[prop], ""); - } - } - - testParts("margin", ""); - testParts("margin", null); - testParts("padding", ""); - testParts("padding", null); - }); - - it("setting undefined to a padding or margin does nothing", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - function testParts(base) { - const props = [base].concat(parts.map((part) => base + part)); - for (const prop of props) { - style[prop] = "10px"; - assert.strictEqual(style[prop], "10px"); - style[prop] = undefined; - assert.strictEqual(style[prop], "10px"); + it("item() throws if argument is not given", () => { + const style = new CSSStyleDeclaration(window); + assert.throws( + () => { + style.item(); + }, + (e) => { + assert.strictEqual(e instanceof window.TypeError, true); + assert.strictEqual(e.message, "1 argument required, but only 0 present."); + return true; } - } - - testParts("margin"); - testParts("padding"); - }); - - it("setting null to background works", () => { - const style = new CSSStyleDeclaration(); - style.background = "red"; - assert.strictEqual(style.cssText, "background: red;"); - style.background = null; - assert.strictEqual(style.cssText, ""); - }); - - it("flex properties should keep their values", () => { - const style = new CSSStyleDeclaration(); - style.flexDirection = "column"; - assert.strictEqual(style.cssText, "flex-direction: column;"); - style.flexDirection = "row"; - assert.strictEqual(style.cssText, "flex-direction: row;"); + ); }); - it("camelcase properties are not assigned with `.setproperty()`", () => { - const style = new CSSStyleDeclaration(); + it("camelcase properties are not assigned with setproperty()", () => { + const style = new CSSStyleDeclaration(window); style.setProperty("fontSize", "12px"); assert.strictEqual(style.cssText, ""); }); - it("casing is ignored in `.setproperty()`", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("FoNt-SiZe", "12px"); - assert.strictEqual(style.fontSize, "12px"); - assert.strictEqual(style.getPropertyValue("font-size"), "12px"); - }); - - it("support non string entries in border-spacing", () => { - const style = new CSSStyleDeclaration(); - style.borderSpacing = 0; - assert.strictEqual(style.cssText, "border-spacing: 0px;"); - }); - - it("float should be valid property for `.setproperty()`", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("float", "left"); - assert.strictEqual(style.float, "left"); - assert.strictEqual(style.getPropertyValue("float"), "left"); - }); - - it("flex-shrink works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex-shrink", 0); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - style.setProperty("flex-shrink", 1); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.cssText, "flex-shrink: 1;"); - }); - - it("flex-grow works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex-grow", 2); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.cssText, "flex-grow: 2;"); - }); - - it("flex-basis works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex-basis", 0); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); - style.setProperty("flex-basis", "250px"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); - style.setProperty("flex-basis", "10em"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "10em"); - style.setProperty("flex-basis", "30%"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "30%"); - assert.strictEqual(style.cssText, "flex-basis: 30%;"); - }); - - it("shorthand flex works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex", "none"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.removeProperty("flex-basis"); - style.setProperty("flex", "auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "0 1 250px"); - assert.strictEqual(style.getPropertyValue("flex"), "0 1 250px"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); - style.removeProperty("flex"); - style.setProperty("flex", "0 0 auto"); - assert.strictEqual(style.getPropertyValue("flex"), "0 0 auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "0 auto"); - assert.strictEqual(style.getPropertyValue("flex"), "0 1 auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "2"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); - style.removeProperty("flex"); - style.setProperty("flex", "20%"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "20%"); - style.removeProperty("flex"); - style.setProperty("flex", "2 2"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "2"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); - style.removeProperty("flex"); - }); + it("custom properties are case-sensitive", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "--fOo: purple"; - it("font-size get a valid value", () => { - const style = new CSSStyleDeclaration(); - const invalidValue = "1r5px"; - style.cssText = "font-size: 15px"; - assert.strictEqual(1, style.length); - style.cssText = `font-size: ${invalidValue}`; - assert.strictEqual(0, style.length); - assert.strictEqual(undefined, style[0]); + assert.strictEqual(style.getPropertyValue("--foo"), ""); + assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); }); it("getPropertyValue for custom properties in cssText", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.cssText = "--foo: red"; assert.strictEqual(style.getPropertyValue("--foo"), "red"); }); it("getPropertyValue for custom properties with setProperty", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.setProperty("--bar", "blue"); assert.strictEqual(style.getPropertyValue("--bar"), "blue"); }); it("getPropertyValue for custom properties with object setter", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style["--baz"] = "yellow"; assert.strictEqual(style.getPropertyValue("--baz"), ""); }); - it("custom properties are case-sensitive", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "--fOo: purple"; - - assert.strictEqual(style.getPropertyValue("--foo"), ""); - assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); - }); - - for (const property of [ - "width", - "height", - "margin", - "margin-top", - "bottom", - "right", - "padding" - ]) { - it(`supports calc for ${property}`, () => { - const style = new CSSStyleDeclaration(); - style.setProperty(property, "calc(100% - 100px)"); - assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); - }); - } - - it("supports nested calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "calc(100% - calc(200px - 100px))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); - }); - - it("supports nested calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "calc(100% * calc(2 / 3))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); - }); - - it("supports var", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo)"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); - }); - - it("supports var with fallback", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo, 100px)"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); + it("getPropertyPriority for property", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("color", "green", "important"); + assert.strictEqual(style.getPropertyPriority("color"), "important"); }); - it("supports var with var fallback", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo, var(--bar))"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, var(--bar))"); + it("getPropertyPriority for custom property", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green", "important"); + assert.strictEqual(style.getPropertyPriority("--foo"), "important"); }); - it("supports calc with var inside", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "calc(100% - var(--foo))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(100% - var(--foo))"); - }); - - it("supports var with calc inside", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo, calc(var(--bar) + 3px))"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, calc(var(--bar) + 3px))"); - }); - - it("supports color var", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("color", "var(--foo)"); - assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); - }); - - it("should not normalize if var() is included", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); - assert.strictEqual( - style.getPropertyValue("line-height"), - "calc( /* comment */ 100% - calc(var(--foo) *2 ))" - ); - }); - - it("supports abs", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "abs(1 - 2 * 3)"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(5)"); - }); - - it("supports abs inside calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "calc(abs(1) + abs(2))"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); - }); - - it("supports sign", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "sign(.1)"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); - }); - - it("supports sign inside calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "calc(sign(.1) + sign(.2))"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(2)"); - }); - - it("no-op for setting undefined to width", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "10px"); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.setProperty("width", undefined); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.width = undefined; - assert.strictEqual(style.getPropertyValue("width"), "10px"); - }); - - it("shorthand serialization with shorthand and longhands mixed", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: blue; background: red !important; background-color: green;"; - assert.strictEqual(style.cssText, "background: red !important;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleDeclaration(); - style.cssText = - "border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;"; - assert.strictEqual(style.cssText, "border: 1px;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border-width: 1px;"; - assert.strictEqual(style.cssText, "border-width: 1px;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border: 1px; border-top: 1px !important;"; - assert.strictEqual( - style.cssText, - "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;" - ); - }); - - it("set cssText as none", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border: none;"; - assert.strictEqual(style.cssText, "border: medium;"); - }); - - it("invalid cssText should be parsed", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "color: red; }"; - assert.strictEqual(style.cssText, "color: red;"); - }); - - it("single value flex with CSS-wide keyword", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial;"; - assert.strictEqual(style.flex, "initial"); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "initial"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex: initial;"); - }); - - it("single value flex with non-CSS-wide value", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: 0;"; - assert.strictEqual(style.flex, "0 1 0%"); - assert.strictEqual(style.flexGrow, "0"); - assert.strictEqual(style.flexShrink, "1"); - assert.strictEqual(style.flexBasis, "0%"); - assert.strictEqual(style.cssText, "flex: 0 1 0%;"); - }); - - it("multiple values flex with CSS-wide keyword", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial; flex-basis: initial; flex-shrink: initial;"; - assert.strictEqual(style.flex, "initial"); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "initial"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex: initial;"); - }); - - it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial; flex-shrink: 0;"; - assert.strictEqual(style.flex, ""); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "0"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;"); - }); - - it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; - assert.strictEqual(style.flex, ""); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "2"); - assert.strictEqual(style.flexBasis, "0px"); - assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;"); - }); -}); - -/* regression tests */ -describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { - it("should set global value unset", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "10px"); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.setProperty("width", "unset"); - assert.strictEqual(style.getPropertyValue("width"), "unset"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { - it("should not set custom properties twice", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("--foo", 0); - style.setProperty("--foo", 1); - - assert.strictEqual(style.length, 1); - assert.strictEqual(style.item(0), "--foo"); - assert.strictEqual(style.item(1), ""); - assert.deepEqual(JSON.parse(JSON.stringify(style)), { - 0: "--foo" + it("setProperty throws if read-only flag is set", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green"); + style.setOptions({ + readOnly: true }); - assert.strictEqual(style.getPropertyValue("--foo"), "1"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { - it("should set stringified value", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("--foo", true); - assert.strictEqual(style.getPropertyValue("--foo"), "true"); - }); - - it("throws for setting Symbol", () => { - const style = new CSSStyleDeclaration(); assert.throws( - () => style.setProperty("width", Symbol("foo")), + () => { + style.setProperty("--foo", "red"); + }, (e) => { - assert.strictEqual(e instanceof TypeError, true); - assert.strictEqual(e.message, "Can not convert symbol to string."); + assert.strictEqual(e instanceof window.DOMException, true); + assert.strictEqual(e.name, "NoModificationAllowedError"); + assert.strictEqual(e.message, "Property --foo can not be modified."); return true; } ); + }); + + it("removeProperty throws if read-only flag is set", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green"); + style.setProperty("--bar", "red"); + assert.strictEqual(style.removeProperty("--foo"), "green"); + style.setOptions({ + readOnly: true + }); assert.throws( () => { - style.width = Symbol("foo"); + style.removeProperty("--bar"); }, (e) => { - assert.strictEqual(e instanceof TypeError, true); - assert.strictEqual(e.message, "Can not convert symbol to string."); + assert.strictEqual(e instanceof window.DOMException, true); + assert.strictEqual(e.name, "NoModificationAllowedError"); + assert.strictEqual(e.message, "Property --bar can not be modified."); return true; } ); }); }); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { - it('returns empty string for "webkit-*", without leading "-"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: green; webkit-transform: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitTransform, ""); - }); - - it('should set/get value for "-webkit-*"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: green; -webkit-transform: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitTransform, "scale(3)"); - }); - - it('returns undefined for unknown "-webkit-*"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: green; -webkit-foo: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitFoo, undefined); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { - it("no-op when setting undefined to border", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px solid green"; - assert.strictEqual(style.border, "1px solid green"); - style.border = undefined; - assert.strictEqual(style.border, "1px solid green"); - }); - - it("no-op when setting undefined to borderWidth", () => { - const style = new CSSStyleDeclaration(); - style.borderWidth = "1px"; - assert.strictEqual(style.borderWidth, "1px"); - style.border = undefined; - assert.strictEqual(style.borderWidth, "1px"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () => { - it("should support keywords", () => { - const keywords = [ - "serif", - "sans-serif", - "cursive", - "fantasy", - "monospace", - "system-ui", - "math", - "ui-serif", - "ui-sans-serif", - "ui-monospace", - "ui-rounded" - ]; - const style = new CSSStyleDeclaration(); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - it("should support generic() function keywords", () => { - const keywords = [ - "generic(fangsong)", - "generic(kai)", - "generic(khmer-mul)", - "generic(nastaliq)" - ]; - const style = new CSSStyleDeclaration(); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - // see https://drafts.csswg.org/css-fonts-4/#changes-2021-12-21 - it("should support removed generic keywords as non generic family name", () => { - const keywords = ["emoji", "fangsong"]; - const style = new CSSStyleDeclaration(); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - it("should support `-webkit-` prefixed family name", () => { - const style = new CSSStyleDeclaration(); - style.fontFamily = "-webkit-body"; - assert.strictEqual(style.fontFamily, "-webkit-body"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { - it("should get normalized value for font shorthand", () => { - const style = new CSSStyleDeclaration(); - style.font = "normal bold 4px sans-serif"; - assert.strictEqual(style.font, "bold 4px sans-serif"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { - it("should return value for each property", () => { - const style = new CSSStyleDeclaration(); - const key = "background-color"; - const camel = "backgroundColor"; - const value = "var(--foo)"; - style[key] = value; - assert.strictEqual(style[key], value); - style[key] = null; - style[camel] = value; - assert.strictEqual(style[camel], value); - }); - - it("should set var() values for background-attachment correctly", () => { - const style = new CSSStyleDeclaration(); - style.backgroundAttachment = "var(--foo)"; - assert.strictEqual(style.backgroundAttachment, "var(--foo)"); - style.setProperty("background-attachment", "var(--bar)"); - assert.strictEqual(style.backgroundAttachment, "var(--bar)"); - }); -}); - -describe("propertyList", () => { - it("should get property list", () => { - assert.deepEqual(propertyList, Object.fromEntries(implementedProperties)); - }); -}); diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js new file mode 100644 index 00000000..8f92dede --- /dev/null +++ b/test/CSSStyleProperties.test.js @@ -0,0 +1,1533 @@ +"use strict"; + +const { describe, it } = require("node:test"); +const assert = require("node:assert/strict"); +const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); +const { CSSStyleProperties } = require("../lib/CSSStyleProperties"); +const propertyList = require("../lib/generated/propertyList"); +const camelize = require("../scripts/camelize"); + +const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError +}; + +describe("CSSStyleProperties", () => { + it("is instanceof CSSStyleDeclaration", () => { + const style = new CSSStyleProperties(window); + assert.strictEqual(style instanceof CSSStyleDeclaration, true); + }); + + it("all dashed properties are included in propertyList", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { + assert.strictEqual(propertyList.has(i), true, i); + } + } + }); + + it("has camelCased property for dashed property", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { + const camel = camelize.dashedToCamelCase(i); + assert.ok(style[camel] !== undefined, i); + } + } + }); + + // FIXME: https://github.com/jsdom/cssstyle/issues/210 + it.skip("all webkit prefixed properties are included in propertyList", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { + assert.strictEqual(propertyList.has(i), true, i); + } + } + }); + + it("has camelCased property for webkit prefixed property", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { + const camel = camelize.dashedToCamelCase(i); + assert.ok(style[camel] !== undefined, i); + } + } + }); + + it("has PascalCased property for webkit prefixed property", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^webkit[A-Z]/.test(i)) { + const pascal = i.replace(/^webkit/, "Webkit"); + assert.ok(style[pascal] !== undefined); + } + } + }); + + it("setting cssFloat should also set float", () => { + const style = new CSSStyleProperties(window); + style.cssFloat = "left"; + assert.strictEqual(style.cssFloat, "left"); + assert.strictEqual(style.float, "left"); + }); + + it("setting float should also set cssfloat", () => { + const style = new CSSStyleProperties(window); + style.float = "left"; + assert.strictEqual(style.cssFloat, "left"); + assert.strictEqual(style.float, "left"); + }); + + it("from style string", () => { + const style = new CSSStyleProperties(window); + style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; + assert.strictEqual(style.length, 4); + assert.strictEqual( + style.cssText, + "color: blue; background-color: red; width: 78%; height: 50vh;" + ); + assert.strictEqual(style.getPropertyValue("color"), "blue"); + assert.strictEqual(style.item(0), "color"); + assert.strictEqual(style[1], "background-color"); + assert.strictEqual(style.backgroundColor, "red"); + style.cssText = ""; + assert.strictEqual(style.cssText, ""); + assert.strictEqual(style.length, 0); + }); + + it("from properties", () => { + const style = new CSSStyleProperties(window); + style.color = "blue"; + assert.strictEqual(style.length, 1); + assert.strictEqual(style[0], "color"); + assert.strictEqual(style.cssText, "color: blue;"); + assert.strictEqual(style.item(0), "color"); + assert.strictEqual(style.color, "blue"); + style.backgroundColor = "red"; + assert.strictEqual(style.length, 2); + assert.strictEqual(style[0], "color"); + assert.strictEqual(style[1], "background-color"); + assert.strictEqual(style.cssText, "color: blue; background-color: red;"); + assert.strictEqual(style.backgroundColor, "red"); + style.removeProperty("color"); + assert.strictEqual(style[0], "background-color"); + }); + + it("shorthand properties", () => { + const style = new CSSStyleProperties(window); + style.background = "blue url(http://www.example.com/some_img.jpg)"; + assert.strictEqual(style.backgroundColor, "blue"); + assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); + assert.strictEqual(style.background, 'url("http://www.example.com/some_img.jpg") blue'); + style.border = "0 solid black"; + assert.strictEqual(style.borderWidth, "0px"); + assert.strictEqual(style.borderStyle, "solid"); + assert.strictEqual(style.borderColor, "black"); + assert.strictEqual(style.borderTopWidth, "0px"); + assert.strictEqual(style.borderLeftStyle, "solid"); + assert.strictEqual(style.borderBottomColor, "black"); + style.font = "12em monospace"; + assert.strictEqual(style.fontSize, "12em"); + assert.strictEqual(style.fontFamily, "monospace"); + }); + + it("width and height properties and null and empty strings", () => { + const style = new CSSStyleProperties(window); + style.height = 6; + assert.strictEqual(style.height, ""); + style.width = 0; + assert.strictEqual(style.width, "0px"); + style.height = "34%"; + assert.strictEqual(style.height, "34%"); + style.height = "100vh"; + assert.strictEqual(style.height, "100vh"); + style.height = "100vw"; + assert.strictEqual(style.height, "100vw"); + style.height = ""; + assert.strictEqual(style.length, 1); + assert.strictEqual(style.cssText, "width: 0px;"); + style.width = null; + assert.strictEqual(style.length, 0); + assert.strictEqual(style.cssText, ""); + }); + + it("implicit properties", () => { + const style = new CSSStyleProperties(window); + style.borderWidth = 0; + assert.strictEqual(style.border, ""); + assert.strictEqual(style.borderWidth, "0px"); + assert.strictEqual(style.borderTopWidth, "0px"); + assert.strictEqual(style.borderBottomWidth, "0px"); + assert.strictEqual(style.borderLeftWidth, "0px"); + assert.strictEqual(style.borderRightWidth, "0px"); + assert.strictEqual(style.cssText, "border-width: 0px;"); + }); + + it("top, left, right, bottom properties", () => { + const style = new CSSStyleProperties(window); + style.top = 0; + style.left = "0%"; + style.right = "5em"; + style.bottom = "12pt"; + assert.strictEqual(style.top, "0px"); + assert.strictEqual(style.left, "0%"); + assert.strictEqual(style.right, "5em"); + assert.strictEqual(style.bottom, "12pt"); + assert.strictEqual(style.length, 4); + assert.strictEqual(style.cssText, "top: 0px; left: 0%; right: 5em; bottom: 12pt;"); + }); + + it('top, left, right, bottom properties should accept "auto"', () => { + const style = new CSSStyleProperties(window); + style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; + assert.strictEqual(style.top, "auto"); + assert.strictEqual(style.right, "auto"); + assert.strictEqual(style.bottom, "auto"); + assert.strictEqual(style.left, "auto"); + }); + + it("clear and clip properties", () => { + const style = new CSSStyleProperties(window); + style.clear = "none"; + assert.strictEqual(style.clear, "none"); + style.clear = "lfet"; + assert.strictEqual(style.clear, "none"); + style.clear = "left"; + assert.strictEqual(style.clear, "left"); + style.clear = "right"; + assert.strictEqual(style.clear, "right"); + style.clear = "both"; + assert.strictEqual(style.clear, "both"); + style.clip = "elipse(5px, 10px)"; + assert.strictEqual(style.clip, ""); + assert.strictEqual(style.length, 1); + style.clip = "rect(0, 3Em, 2pt, 50%)"; + assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 50%)"); + assert.strictEqual(style.length, 2); + assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); + }); + + it("colors", () => { + const style = new CSSStyleProperties(window); + style.color = "rgba(0,0,0,0)"; + assert.strictEqual(style.color, "rgba(0, 0, 0, 0)"); + style.color = "rgba(5%, 10%, 20%, 0.4)"; + assert.strictEqual(style.color, "rgba(13, 26, 51, 0.4)"); + style.color = "rgb(33%, 34%, 33%)"; + assert.strictEqual(style.color, "rgb(84, 87, 84)"); + style.color = "rgba(300, 200, 100, 1.5)"; + assert.strictEqual(style.color, "rgb(255, 200, 100)"); + style.color = "hsla(0, 1%, 2%, 0.5)"; + assert.strictEqual(style.color, "rgba(5, 5, 5, 0.5)"); + style.color = "hsl(0, 1%, 2%)"; + assert.strictEqual(style.color, "rgb(5, 5, 5)"); + style.color = "rebeccapurple"; + assert.strictEqual(style.color, "rebeccapurple"); + style.color = "transparent"; + assert.strictEqual(style.color, "transparent"); + style.color = "currentcolor"; + assert.strictEqual(style.color, "currentcolor"); + style.color = "#ffffffff"; + assert.strictEqual(style.color, "rgb(255, 255, 255)"); + style.color = "#fffa"; + assert.strictEqual(style.color, "rgba(255, 255, 255, 0.667)"); + style.color = "#ffffff66"; + assert.strictEqual(style.color, "rgba(255, 255, 255, 0.4)"); + }); + + it("invalid hex color value", () => { + const style = new CSSStyleProperties(window); + style.color = "#1234567"; + assert.strictEqual(style.color, ""); + }); + + it("shorthand properties with embedded spaces", () => { + let style = new CSSStyleProperties(window); + style.background = "rgb(0, 0, 0) url(/something/somewhere.jpg)"; + assert.strictEqual(style.backgroundColor, "rgb(0, 0, 0)"); + assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")'); + assert.strictEqual(style.cssText, 'background: url("/something/somewhere.jpg") rgb(0, 0, 0);'); + style = new CSSStyleProperties(window); + style.border = " 1px solid black "; + assert.strictEqual(style.border, "1px solid black"); + }); + + it("setting shorthand properties to an empty string should clear all dependent properties", () => { + const style = new CSSStyleProperties(window); + style.borderWidth = "1px"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("setting implicit properties to an empty string should clear all dependent properties", () => { + const style = new CSSStyleProperties(window); + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderWidth = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { + const style = new CSSStyleProperties(window); + style.borderTopWidth = "2px"; + assert.strictEqual(style.cssText, "border-top-width: 2px;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + style.borderTop = "2px solid black"; + assert.strictEqual(style.cssText, "border-top: 2px solid black;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("set border as none", () => { + const style = new CSSStyleProperties(window); + style.border = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border as none", () => { + const style = new CSSStyleProperties(window); + style.border = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border-style as none", () => { + const style = new CSSStyleProperties(window); + style.borderStyle = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual(style.cssText, "border-style: none;", "cssText"); + }); + + it("set border-top as none", () => { + const style = new CSSStyleProperties(window); + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual(style.cssText, "border-top: medium;", "cssText"); + }); + + it("set border as 1px and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "1px", "border"); + assert.strictEqual(style.borderWidth, "1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "1px", "border-top"); + assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: 1px;", "cssText"); + }); + + it("set border as 1px and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "1px", "border"); + assert.strictEqual(style.borderWidth, "1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "1px", "border-top"); + assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: 1px;", "cssText"); + }); + + it("set border as 1px and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as 1px solid and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none solid solid; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as none and change border-style to null", () => { + const style = new CSSStyleProperties(window); + style.border = "none"; + style.borderStyle = null; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as solid and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.border = "solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium; border-style: none solid solid; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as solid and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.border = "solid"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border-style as solid and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.borderStyle = "solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual( + style.cssText, + "border-style: none solid solid; border-top-width: medium; border-top-color: currentcolor;", + "cssText" + ); + }); + + it("set border-top as solid and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.borderTop = "solid"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual( + style.cssText, + "border-top-width: medium; border-top-color: currentcolor; border-style: none;", + "cssText" + ); + }); + + it("set border-style as solid and change border-top to null", () => { + const style = new CSSStyleProperties(window); + style.borderStyle = "solid"; + style.borderTop = null; + assert.strictEqual( + style.cssText, + "border-right-style: solid; border-bottom-style: solid; border-left-style: solid;", + "cssText" + ); + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + }); + + it("setting border values to none should change dependent values", () => { + const style = new CSSStyleProperties(window); + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.border = "none"; + assert.strictEqual(style.border, "medium"); + assert.strictEqual(style.borderTop, "medium"); + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "medium"); + assert.strictEqual(style.cssText, "border: medium;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderStyle = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + assert.strictEqual(style.cssText, "border-top-width: 1px; border-style: none;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderTop = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "medium"); + assert.strictEqual(style.cssText, "border-top: medium;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderTopStyle = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + assert.strictEqual(style.cssText, "border-top-width: 1px; border-top-style: none;"); + + style.border = null; + style.borderImage = null; + style.border = "1px"; + assert.strictEqual(style.cssText, "border: 1px;"); + assert.strictEqual(style.border, "1px"); + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + style.borderTop = "none"; + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;" + ); + }); + + it("setting border to green", () => { + const style = new CSSStyleProperties(window); + style.border = "green"; + assert.strictEqual(style.cssText, "border: green;"); + assert.strictEqual(style.border, "green"); + }); + + it("setting border to green", () => { + const style = new CSSStyleProperties(window); + style.border = "green"; + assert.strictEqual(style.cssText, "border: green;"); + assert.strictEqual(style.border, "green"); + }); + + it("setting border to initial should set all properties initial", () => { + const style = new CSSStyleProperties(window); + style.border = "initial"; + assert.strictEqual(style.cssText, "border: initial;"); + assert.strictEqual(style.border, "initial"); + assert.strictEqual(style.borderWidth, "initial"); + assert.strictEqual(style.borderStyle, "initial"); + assert.strictEqual(style.borderColor, "initial"); + assert.strictEqual(style.borderTop, "initial"); + assert.strictEqual(style.borderTopWidth, "initial"); + assert.strictEqual(style.borderTopStyle, "initial"); + assert.strictEqual(style.borderTopColor, "initial"); + assert.strictEqual(style.borderImage, "none"); + }); + + it("setting borderTop to initial should set top related properties initial", () => { + const style = new CSSStyleProperties(window); + style.borderTop = "initial"; + assert.strictEqual(style.cssText, "border-top: initial;"); + assert.strictEqual(style.border, ""); + assert.strictEqual(style.borderWidth, ""); + assert.strictEqual(style.borderStyle, ""); + assert.strictEqual(style.borderColor, ""); + assert.strictEqual(style.borderTop, "initial"); + assert.strictEqual(style.borderTopWidth, "initial"); + assert.strictEqual(style.borderTopStyle, "initial"); + assert.strictEqual(style.borderTopColor, "initial"); + assert.strictEqual(style.borderImage, ""); + }); + + it("setting border to 0 should be okay", () => { + const style = new CSSStyleProperties(window); + style.border = 0; + assert.strictEqual(style.cssText, "border: 0px;"); + assert.strictEqual(style.border, "0px"); + }); + + it("setting borderColor to var() should be okay", () => { + const style = new CSSStyleProperties(window); + style.borderColor = "var(--foo)"; + assert.strictEqual(style.cssText, "border-color: var(--foo);"); + }); + + it("setting borderColor to inherit should be okay", () => { + const style = new CSSStyleProperties(window); + style.borderColor = "inherit"; + assert.strictEqual(style.cssText, "border-color: inherit;"); + }); + + it("setting values implicit and shorthand properties via csstext and setproperty should propagate to dependent properties", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border: 1px solid black;"; + assert.strictEqual(style.cssText, "border: 1px solid black;"); + assert.strictEqual(style.borderTop, "1px solid black"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + style.setProperty("border", "1px solid black"); + assert.strictEqual(style.cssText, "border: 1px solid black;"); + }); + + it("setting opacity should work", () => { + const style = new CSSStyleProperties(window); + style.setProperty("opacity", 0.75); + assert.strictEqual(style.cssText, "opacity: 0.75;"); + style.opacity = "0.50"; + assert.strictEqual(style.cssText, "opacity: 0.5;"); + style.opacity = 1; + assert.strictEqual(style.cssText, "opacity: 1;"); + }); + + it("width and height of auto should work", () => { + let style = new CSSStyleProperties(window); + style.width = "auto"; + assert.strictEqual(style.cssText, "width: auto;"); + assert.strictEqual(style.width, "auto"); + style = new CSSStyleProperties(window); + style.height = "auto"; + assert.strictEqual(style.cssText, "height: auto;"); + assert.strictEqual(style.height, "auto"); + }); + + it("Shorthand serialization with just longhands", () => { + const style = new CSSStyleProperties(window); + style.cssText = "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;"; + assert.strictEqual(style.cssText, "margin: 10px;"); + assert.strictEqual(style.margin, "10px"); + + style.cssText = + "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;"; + assert.strictEqual( + style.cssText, + "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;" + ); + assert.strictEqual(style.margin, ""); + + style.cssText = + "margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;"; + assert.strictEqual(style.cssText, "margin: 10px !important;"); + assert.strictEqual(style.margin, "10px"); + }); + + it("padding and margin should set/clear shorthand properties", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const testParts = function (name, v, V) { + style[name] = v; + for (let i = 0; i < 4; i++) { + const part = name + parts[i]; + assert.strictEqual(style[part], V[i]); + } + + assert.strictEqual(style[name], v); + style[name] = ""; + }; + testParts("padding", "1px", ["1px", "1px", "1px", "1px"]); + testParts("padding", "1px 2%", ["1px", "2%", "1px", "2%"]); + testParts("padding", "1px 2px 3px", ["1px", "2px", "3px", "2px"]); + testParts("padding", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); + style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = "1px"; + testParts("padding", "", ["", "", "", ""]); + testParts("margin", "1px", ["1px", "1px", "1px", "1px"]); + testParts("margin", "1px auto", ["1px", "auto", "1px", "auto"]); + testParts("margin", "1px 2% 3px", ["1px", "2%", "3px", "2%"]); + testParts("margin", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); + style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = "1px"; + testParts("margin", "", ["", "", "", ""]); + }); + + it("padding and margin shorthands should set main properties", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const testParts = function (name, v, V) { + let expected; + for (let i = 0; i < 4; i++) { + style[name] = v; + style[name + parts[i]] = V; + expected = v.split(/ /); + expected[i] = V; + expected = expected.join(" "); + + assert.strictEqual(style[name], expected); + } + }; + testParts("padding", "1px 2px 3px 4px", "10px"); + testParts("margin", "1px 2px 3px 4px", "10px"); + testParts("margin", "1px 2px 3px 4px", "auto"); + }); + + it("setting individual padding and margin properties to an empty string should clear them", () => { + const style = new CSSStyleProperties(window); + + const properties = ["padding", "margin"]; + const parts = ["Top", "Right", "Bottom", "Left"]; + for (let i = 0; i < properties.length; i++) { + for (let j = 0; j < parts.length; j++) { + const property = properties[i] + parts[j]; + style[property] = "12px"; + assert.strictEqual(style[property], "12px"); + + style[property] = ""; + assert.strictEqual(style[property], ""); + } + } + }); + + it("removing and setting individual margin properties updates the combined property accordingly", () => { + const style = new CSSStyleProperties(window); + style.margin = "1px 2px 3px 4px"; + style.marginTop = ""; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginBottom, "3px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginBottom = ""; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginBottom = "5px"; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginBottom, "5px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginTop = "6px"; + assert.strictEqual(style.cssText, "margin: 6px 2px 5px 4px;"); + }); + + for (const property of ["padding", "margin"]) { + it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const partValues = ["1px", "2px", "3px", "4px"]; + + for (let j = 0; j < parts.length; j++) { + const partToRemove = parts[j]; + style[property] = partValues.join(" "); + style[property + partToRemove] = ""; + + // Main property should have been removed + assert.strictEqual(style[property], ""); + + // Expect other parts to still be there + for (let k = 0; k < parts.length; k++) { + const propertyCss = `${property}-${parts[k].toLowerCase()}: ${partValues[k]};`; + if (k === j) { + assert.strictEqual(style[property + parts[k]], ""); + assert.strictEqual(style.cssText.includes(propertyCss), false); + } else { + assert.strictEqual(style[property + parts[k]], partValues[k]); + assert.strictEqual(style.cssText.includes(propertyCss), true); + } + } + } + }); + + it(`setting additional ${property} properties keeps important status of others`, () => { + const style = new CSSStyleProperties(window); + const importantProperty = `${property}-top: 3px !important;`; + style.cssText = importantProperty; + assert.strictEqual(style.cssText.includes(importantProperty), true); + + style[`${property}Right`] = "4px"; + style[`${property}Bottom`] = "5px"; + style[`${property}Left`] = "6px"; + assert.strictEqual(style.cssText.includes(importantProperty), true); + assert.strictEqual(style.cssText.includes(`${property}-right: 4px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-bottom: 5px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-left: 6px;`), true); + assert.strictEqual(style.cssText.includes("margin:"), false); + }); + + it(`setting individual ${property} keeps important status of others`, () => { + const style = new CSSStyleProperties(window); + style.cssText = `${property}: 3px !important;`; + style[`${property}Top`] = "4px"; + assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); + assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); + assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); + assert.strictEqual(style.cssText.includes("margin:"), false); + }); + } + + it("setting a value to 0 should return the string value", () => { + const style = new CSSStyleProperties(window); + style.setProperty("fill-opacity", 0); + assert.strictEqual(style.fillOpacity, "0"); + }); + + it("onchange callback should be called when the csstext changes", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: (cssText) => { + called++; + assert.strictEqual(cssText, "opacity: 0;"); + } + }); + style.cssText = "opacity: 0;"; + assert.strictEqual(called, 1); + style.cssText = "opacity: 0;"; + assert.strictEqual(called, 2); + }); + + it("onchange callback should be called only once when multiple properties were added", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: (cssText) => { + called++; + assert.strictEqual(cssText, "width: 100px; height: 100px;"); + } + }); + style.cssText = "width: 100px;height:100px;"; + assert.strictEqual(called, 1); + }); + + it("onchange callback should not be called when property is set to the same value", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: () => { + called++; + } + }); + + style.setProperty("opacity", 0); + assert.strictEqual(called, 1); + style.setProperty("opacity", 0); + assert.strictEqual(called, 1); + }); + + it("onchange callback should not be called when removeProperty was called on non-existing property", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: () => { + called++; + } + }); + style.removeProperty("opacity"); + assert.strictEqual(called, 0); + }); + + it("setting improper css to csstext should not throw", () => { + const style = new CSSStyleProperties(window); + style.cssText = "color: "; + assert.strictEqual(style.cssText, ""); + style.color = "black"; + style.cssText = "float: "; + assert.strictEqual(style.cssText, ""); + }); + + it("url parsing works with quotes", () => { + const style = new CSSStyleProperties(window); + style.backgroundImage = "url(http://some/url/here1.png)"; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); + style.backgroundImage = "url('http://some/url/here2.png')"; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); + style.backgroundImage = 'url("http://some/url/here3.png")'; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); + }); + + it("setting 0 to a padding or margin works", () => { + const style = new CSSStyleProperties(window); + style.padding = 0; + assert.strictEqual(style.cssText, "padding: 0px;"); + style.margin = "1em"; + style.marginTop = "0"; + assert.strictEqual(style.marginTop, "0px"); + }); + + it("setting ex units to a padding or margin works", () => { + const style = new CSSStyleProperties(window); + style.padding = "1ex"; + assert.strictEqual(style.cssText, "padding: 1ex;"); + style.margin = "1em"; + style.marginTop = "0.5ex"; + assert.strictEqual(style.marginTop, "0.5ex"); + }); + + it("setting empty string and null to a padding or margin works", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + function testParts(base, nullValue) { + const props = [base].concat(parts.map((part) => base + part)); + for (const prop of props) { + assert.strictEqual(style[prop], ""); + style[prop] = "10px"; + assert.strictEqual(style[prop], "10px"); + style[prop] = nullValue; + assert.strictEqual(style[prop], ""); + } + } + + testParts("margin", ""); + testParts("margin", null); + testParts("padding", ""); + testParts("padding", null); + }); + + it("setting undefined to a padding or margin does nothing", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + function testParts(base) { + const props = [base].concat(parts.map((part) => base + part)); + for (const prop of props) { + style[prop] = "10px"; + assert.strictEqual(style[prop], "10px"); + style[prop] = undefined; + assert.strictEqual(style[prop], "10px"); + } + } + + testParts("margin"); + testParts("padding"); + }); + + it("setting null to background works", () => { + const style = new CSSStyleProperties(window); + style.background = "red"; + assert.strictEqual(style.cssText, "background: red;"); + style.background = null; + assert.strictEqual(style.cssText, ""); + }); + + it("flex properties should keep their values", () => { + const style = new CSSStyleProperties(window); + style.flexDirection = "column"; + assert.strictEqual(style.cssText, "flex-direction: column;"); + style.flexDirection = "row"; + assert.strictEqual(style.cssText, "flex-direction: row;"); + }); + + it("camelcase properties are not assigned with `.setproperty()`", () => { + const style = new CSSStyleProperties(window); + style.setProperty("fontSize", "12px"); + assert.strictEqual(style.cssText, ""); + }); + + it("casing is ignored in `.setproperty()`", () => { + const style = new CSSStyleProperties(window); + style.setProperty("FoNt-SiZe", "12px"); + assert.strictEqual(style.fontSize, "12px"); + assert.strictEqual(style.getPropertyValue("font-size"), "12px"); + }); + + it("support non string entries in border-spacing", () => { + const style = new CSSStyleProperties(window); + style.borderSpacing = 0; + assert.strictEqual(style.cssText, "border-spacing: 0px;"); + }); + + it("float should be valid property for `.setproperty()`", () => { + const style = new CSSStyleProperties(window); + style.setProperty("float", "left"); + assert.strictEqual(style.float, "left"); + assert.strictEqual(style.getPropertyValue("float"), "left"); + }); + + it("flex-shrink works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex-shrink", 0); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); + style.setProperty("flex-shrink", 1); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.cssText, "flex-shrink: 1;"); + }); + + it("flex-grow works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex-grow", 2); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.cssText, "flex-grow: 2;"); + }); + + it("flex-basis works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex-basis", 0); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); + style.setProperty("flex-basis", "250px"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); + style.setProperty("flex-basis", "10em"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "10em"); + style.setProperty("flex-basis", "30%"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "30%"); + assert.strictEqual(style.cssText, "flex-basis: 30%;"); + }); + + it("shorthand flex works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex", "none"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); + style.removeProperty("flex"); + style.removeProperty("flex-basis"); + style.setProperty("flex", "auto"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); + style.removeProperty("flex"); + style.setProperty("flex", "0 1 250px"); + assert.strictEqual(style.getPropertyValue("flex"), "0 1 250px"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); + style.removeProperty("flex"); + style.setProperty("flex", "2"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); + style.removeProperty("flex"); + style.setProperty("flex", "20%"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "20%"); + style.removeProperty("flex"); + style.setProperty("flex", "2 2"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "2"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); + style.removeProperty("flex"); + }); + + it("font-size get a valid value", () => { + const style = new CSSStyleProperties(window); + const invalidValue = "1r5px"; + style.cssText = "font-size: 15px"; + assert.strictEqual(1, style.length); + style.cssText = `font-size: ${invalidValue}`; + assert.strictEqual(0, style.length); + assert.strictEqual(undefined, style[0]); + }); + + it("getPropertyValue for custom properties in cssText", () => { + const style = new CSSStyleProperties(window); + style.cssText = "--foo: red"; + + assert.strictEqual(style.getPropertyValue("--foo"), "red"); + }); + + it("getPropertyValue for custom properties with setProperty", () => { + const style = new CSSStyleProperties(window); + style.setProperty("--bar", "blue"); + + assert.strictEqual(style.getPropertyValue("--bar"), "blue"); + }); + + it("getPropertyValue for custom properties with object setter", () => { + const style = new CSSStyleProperties(window); + style["--baz"] = "yellow"; + + assert.strictEqual(style.getPropertyValue("--baz"), ""); + }); + + it("custom properties are case-sensitive", () => { + const style = new CSSStyleProperties(window); + style.cssText = "--fOo: purple"; + + assert.strictEqual(style.getPropertyValue("--foo"), ""); + assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); + }); + + for (const property of [ + "width", + "height", + "margin", + "margin-top", + "bottom", + "right", + "padding" + ]) { + it(`supports calc for ${property}`, () => { + const style = new CSSStyleProperties(window); + style.setProperty(property, "calc(100% - 100px)"); + assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); + }); + } + + it("supports nested calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "calc(100% - calc(200px - 100px))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); + }); + + it("supports nested calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "calc(100% * calc(2 / 3))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); + }); + + it("supports var", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); + }); + + it("supports var with fallback", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo, 100px)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); + }); + + it("supports var with var fallback", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo, var(--bar))"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, var(--bar))"); + }); + + it("supports calc with var inside", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "calc(100% - var(--foo))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(100% - var(--foo))"); + }); + + it("supports var with calc inside", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo, calc(var(--bar) + 3px))"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, calc(var(--bar) + 3px))"); + }); + + it("supports color var", () => { + const style = new CSSStyleProperties(window); + style.setProperty("color", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); + }); + + it("should not normalize if var() is included", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); + assert.strictEqual( + style.getPropertyValue("line-height"), + "calc( /* comment */ 100% - calc(var(--foo) *2 ))" + ); + }); + + it("supports abs", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "abs(1 - 2 * 3)"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(5)"); + }); + + it("supports abs inside calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "calc(abs(1) + abs(2))"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); + }); + + it("supports sign", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "sign(.1)"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); + }); + + it("supports sign inside calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "calc(sign(.1) + sign(.2))"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(2)"); + }); + + it("no-op for setting undefined to width", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "10px"); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.setProperty("width", undefined); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.width = undefined; + assert.strictEqual(style.getPropertyValue("width"), "10px"); + }); + + it("shorthand serialization with shorthand and longhands mixed", () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: blue; background: red !important; background-color: green;"; + assert.strictEqual(style.cssText, "background: red !important;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(window); + style.cssText = + "border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;"; + assert.strictEqual(style.cssText, "border: 1px;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border-width: 1px;"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border: 1px; border-top: 1px !important;"; + assert.strictEqual( + style.cssText, + "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;" + ); + }); + + it("set cssText as none", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border: none;"; + assert.strictEqual(style.cssText, "border: medium;"); + }); + + it("invalid cssText should be parsed", () => { + const style = new CSSStyleProperties(window); + style.cssText = "color: red; }"; + assert.strictEqual(style.cssText, "color: red;"); + }); + + it("single value flex with CSS-wide keyword", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial;"; + assert.strictEqual(style.flex, "initial"); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "initial"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex: initial;"); + }); + + it("single value flex with non-CSS-wide value", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: 0;"; + assert.strictEqual(style.flex, "0 1 0%"); + assert.strictEqual(style.flexGrow, "0"); + assert.strictEqual(style.flexShrink, "1"); + assert.strictEqual(style.flexBasis, "0%"); + assert.strictEqual(style.cssText, "flex: 0 1 0%;"); + }); + + it("multiple values flex with CSS-wide keyword", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial; flex-basis: initial; flex-shrink: initial;"; + assert.strictEqual(style.flex, "initial"); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "initial"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex: initial;"); + }); + + it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial; flex-shrink: 0;"; + assert.strictEqual(style.flex, ""); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "0"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;"); + }); + + it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; + assert.strictEqual(style.flex, ""); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "2"); + assert.strictEqual(style.flexBasis, "0px"); + assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;"); + }); +}); + +/* regression tests */ +describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { + it("should set global value unset", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "10px"); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.setProperty("width", "unset"); + assert.strictEqual(style.getPropertyValue("width"), "unset"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { + it("should not set custom properties twice", () => { + const style = new CSSStyleProperties(window); + style.setProperty("--foo", 0); + style.setProperty("--foo", 1); + + assert.strictEqual(style.length, 1); + assert.strictEqual(style.item(0), "--foo"); + assert.strictEqual(style.item(1), ""); + assert.strictEqual(style.getPropertyValue("--foo"), "1"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { + it("should set stringified value", () => { + const style = new CSSStyleProperties(window); + style.setProperty("--foo", true); + assert.strictEqual(style.getPropertyValue("--foo"), "true"); + }); + + it("throws for setting Symbol", () => { + const style = new CSSStyleProperties(window); + assert.throws( + () => style.setProperty("width", Symbol("foo")), + (e) => { + assert.strictEqual(e instanceof TypeError, true); + assert.strictEqual(e.message, "Can not convert symbol to string."); + return true; + } + ); + assert.throws( + () => { + style.width = Symbol("foo"); + }, + (e) => { + assert.strictEqual(e instanceof TypeError, true); + assert.strictEqual(e.message, "Can not convert symbol to string."); + return true; + } + ); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { + it('returns empty string for "webkit-*", without leading "-"', () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: green; webkit-transform: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitTransform, ""); + }); + + it('should set/get value for "-webkit-*"', () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: green; -webkit-transform: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitTransform, "scale(3)"); + }); + + it('returns undefined for unknown "-webkit-*"', () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: green; -webkit-foo: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitFoo, undefined); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { + it("no-op when setting undefined to border", () => { + const style = new CSSStyleProperties(window); + style.border = "1px solid green"; + assert.strictEqual(style.border, "1px solid green"); + style.border = undefined; + assert.strictEqual(style.border, "1px solid green"); + }); + + it("no-op when setting undefined to borderWidth", () => { + const style = new CSSStyleProperties(window); + style.borderWidth = "1px"; + assert.strictEqual(style.borderWidth, "1px"); + style.border = undefined; + assert.strictEqual(style.borderWidth, "1px"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () => { + it("should support keywords", () => { + const keywords = [ + "serif", + "sans-serif", + "cursive", + "fantasy", + "monospace", + "system-ui", + "math", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded" + ]; + const style = new CSSStyleProperties(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support generic() function keywords", () => { + const keywords = [ + "generic(fangsong)", + "generic(kai)", + "generic(khmer-mul)", + "generic(nastaliq)" + ]; + const style = new CSSStyleProperties(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + // see https://drafts.csswg.org/css-fonts-4/#changes-2021-12-21 + it("should support removed generic keywords as non generic family name", () => { + const keywords = ["emoji", "fangsong"]; + const style = new CSSStyleProperties(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support `-webkit-` prefixed family name", () => { + const style = new CSSStyleProperties(window); + style.fontFamily = "-webkit-body"; + assert.strictEqual(style.fontFamily, "-webkit-body"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { + it("should get normalized value for font shorthand", () => { + const style = new CSSStyleProperties(window); + style.font = "normal bold 4px sans-serif"; + assert.strictEqual(style.font, "bold 4px sans-serif"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { + it("should return value for each property", () => { + const style = new CSSStyleProperties(window); + const key = "background-color"; + const camel = "backgroundColor"; + const value = "var(--foo)"; + style[key] = value; + assert.strictEqual(style[key], value); + style[key] = null; + style[camel] = value; + assert.strictEqual(style[camel], value); + }); + + it("should set var() values for background-attachment correctly", () => { + const style = new CSSStyleProperties(window); + style.backgroundAttachment = "var(--foo)"; + assert.strictEqual(style.backgroundAttachment, "var(--foo)"); + style.setProperty("background-attachment", "var(--bar)"); + assert.strictEqual(style.backgroundAttachment, "var(--bar)"); + }); + + it("should allow changing a single property on a border, when border contains a css variable", () => { + const style = new CSSStyleProperties(window); + style.border = "0.1rem solid var(--my-color-value)"; + assert.strictEqual(style.border, "0.1rem solid var(--my-color-value)"); + style.borderWidth = "0.2rem"; + assert.strictEqual(style.borderWidth, "0.2rem"); + assert.strictEqual(style.border, ""); + }); + + it("should get value and priority", () => { + const style = new CSSStyleProperties(window); + style.cssText = "word-spacing: 1px !important;"; + assert.strictEqual(style.cssText, "word-spacing: 1px !important;", "cssText"); + assert.strictEqual(style.getPropertyValue("word-spacing"), "1px", "value"); + assert.strictEqual(style.getPropertyPriority("word-spacing"), "important", "priority"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3944", () => { + it("should get overwritten value", () => { + const style = new CSSStyleProperties(window); + style.cssText = + "background:linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center;"; + assert.strictEqual(style.backgroundPosition, "center center"); + style.cssText = + "background:linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center;background-position:top;"; + assert.strictEqual(style.backgroundPosition, "center top"); + }); +}); diff --git a/test/camelize.test.js b/test/camelize.test.js index ce68f424..9d1cb011 100644 --- a/test/camelize.test.js +++ b/test/camelize.test.js @@ -2,7 +2,7 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); -const camelize = require("../lib/utils/camelize"); +const camelize = require("../scripts/camelize"); describe("dashedToCamelCase", () => { it("should not camelize custom property", () => { diff --git a/test/parsers.test.js b/test/parsers.test.js index 2964102a..b98ef630 100644 --- a/test/parsers.test.js +++ b/test/parsers.test.js @@ -267,6 +267,13 @@ describe("resolveCalc", () => { assert.strictEqual(output, "calc(10px + 100vh)"); }); + + it("should return serialized value", () => { + const input = "translate(calc(10%), 10%)"; + const output = parsers.resolveCalc(input); + + assert.strictEqual(output, "translate(calc(10%), 10%)"); + }); }); describe("parseNumber", () => { @@ -979,17 +986,17 @@ describe("parseAngle", () => { }); }); -describe("parseUrl", () => { +describe("parseURL", () => { it("should return undefined", () => { const input = ""; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); it("should return undefined", () => { const input = []; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); @@ -1001,7 +1008,7 @@ describe("parseUrl", () => { value: "foo" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); @@ -1013,7 +1020,7 @@ describe("parseUrl", () => { value: "" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("")'); }); @@ -1025,7 +1032,7 @@ describe("parseUrl", () => { value: "sample.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample.png")'); }); @@ -1037,7 +1044,7 @@ describe("parseUrl", () => { value: "sample\\\\-escaped.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample\\-escaped.png")'); }); @@ -1049,7 +1056,7 @@ describe("parseUrl", () => { value: "sample escaped -space.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample escaped -space.png")'); }); @@ -1061,7 +1068,7 @@ describe("parseUrl", () => { value: "sample\tescaped\t-tab.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample\tescaped\t-tab.png")'); }); @@ -1073,7 +1080,7 @@ describe("parseUrl", () => { value: "sample'escaped'-quote.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); // prettier-ignore assert.strictEqual(output, "url(\"sample'escaped'-quote.png\")"); @@ -1086,7 +1093,7 @@ describe("parseUrl", () => { value: 'sample"escaped"-double-quote.png' } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample\\"escaped\\"-double-quote.png")'); }); @@ -1389,8 +1396,10 @@ describe("parseCSS", () => { it("should get ast", () => { const input = "color: green !important;"; const opt = { - context: "declarationList", - parseValue: false + options: { + context: "declarationList", + parseValue: false + } }; const output = parsers.parseCSS(input, opt); assert.strictEqual(output.type, "DeclarationList"); @@ -1400,8 +1409,10 @@ describe("parseCSS", () => { it("should get ast", () => { const input = "green"; const opt = { - context: "value", - parseValue: false + options: { + context: "value", + parseValue: false + } }; const output = parsers.parseCSS(input, opt); assert.strictEqual(output.type, "Value"); @@ -1411,8 +1422,10 @@ describe("parseCSS", () => { it("should get object", () => { const input = "color: green !important;"; const opt = { - context: "declarationList", - parseValue: false + options: { + context: "declarationList", + parseValue: false + } }; const output = parsers.parseCSS(input, opt, true); const [ diff --git a/test/properties.test.js b/test/properties.test.js index 4594780b..1eedd9ce 100644 --- a/test/properties.test.js +++ b/test/properties.test.js @@ -2,10 +2,16 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); -const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); +const { CSSStyleProperties } = require("../lib/CSSStyleProperties"); + +const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError +}; function testPropertyValue(property, value, expected) { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleProperties(window); let res; style.setProperty(property, value); @@ -34,7 +40,7 @@ function testPropertyValue(property, value, expected) { } function testImplicitPropertyValue(property, value, expected, sub) { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleProperties(); let res; style.setProperty(property, value);