From c8afbb5fc66fd347b9fe7fcb44cdefc4da9d1ae1 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sun, 21 Sep 2025 13:36:33 +0900 Subject: [PATCH 01/18] Split into CSSStyleDeclaration and CSSStyleProperties --- .gitignore | 2 +- lib/CSSStyleDeclaration.js | 214 +- lib/CSSStyleProperties.js | 38 + lib/generated/allProperties.js | 4 +- lib/index.js | 11 + lib/normalize.js | 144 +- lib/parsers.js | 113 +- lib/properties/background.js | 33 +- lib/properties/backgroundAttachment.js | 6 +- lib/properties/backgroundClip.js | 6 +- lib/properties/backgroundColor.js | 8 +- lib/properties/backgroundImage.js | 10 +- lib/properties/backgroundOrigin.js | 6 +- lib/properties/backgroundPosition.js | 30 +- lib/properties/backgroundRepeat.js | 6 +- lib/properties/backgroundSize.js | 8 +- lib/properties/border.js | 18 +- lib/properties/borderBottom.js | 18 +- lib/properties/borderBottomColor.js | 8 +- lib/properties/borderBottomStyle.js | 6 +- lib/properties/borderBottomWidth.js | 14 +- lib/properties/borderCollapse.js | 6 +- lib/properties/borderColor.js | 8 +- lib/properties/borderLeft.js | 18 +- lib/properties/borderLeftColor.js | 8 +- lib/properties/borderLeftStyle.js | 6 +- lib/properties/borderLeftWidth.js | 14 +- lib/properties/borderRight.js | 18 +- lib/properties/borderRightColor.js | 8 +- lib/properties/borderRightStyle.js | 6 +- lib/properties/borderRightWidth.js | 14 +- lib/properties/borderSpacing.js | 12 +- lib/properties/borderStyle.js | 6 +- lib/properties/borderTop.js | 18 +- lib/properties/borderTopColor.js | 8 +- lib/properties/borderTopStyle.js | 6 +- lib/properties/borderTopWidth.js | 14 +- lib/properties/borderWidth.js | 14 +- lib/properties/bottom.js | 8 +- lib/properties/clear.js | 6 +- lib/properties/clip.js | 8 +- lib/properties/color.js | 8 +- lib/properties/display.js | 6 +- lib/properties/flex.js | 15 +- lib/properties/flexBasis.js | 8 +- lib/properties/flexGrow.js | 8 +- lib/properties/flexShrink.js | 8 +- lib/properties/float.js | 6 +- lib/properties/floodColor.js | 8 +- lib/properties/font.js | 33 +- lib/properties/fontFamily.js | 6 +- lib/properties/fontSize.js | 14 +- lib/properties/fontStyle.js | 8 +- lib/properties/fontVariant.js | 6 +- lib/properties/fontWeight.js | 14 +- lib/properties/height.js | 8 +- lib/properties/left.js | 8 +- lib/properties/lightingColor.js | 8 +- lib/properties/lineHeight.js | 20 +- lib/properties/margin.js | 8 +- lib/properties/marginBottom.js | 8 +- lib/properties/marginLeft.js | 8 +- lib/properties/marginRight.js | 8 +- lib/properties/marginTop.js | 8 +- lib/properties/opacity.js | 10 +- lib/properties/outlineColor.js | 8 +- lib/properties/padding.js | 14 +- lib/properties/paddingBottom.js | 14 +- lib/properties/paddingLeft.js | 14 +- lib/properties/paddingRight.js | 14 +- lib/properties/paddingTop.js | 14 +- lib/properties/right.js | 8 +- lib/properties/stopColor.js | 8 +- lib/properties/top.js | 8 +- lib/properties/webkitBorderAfterColor.js | 8 +- lib/properties/webkitBorderBeforeColor.js | 8 +- lib/properties/webkitBorderEndColor.js | 8 +- lib/properties/webkitBorderStartColor.js | 8 +- lib/properties/webkitColumnRuleColor.js | 8 +- lib/properties/webkitTapHighlightColor.js | 8 +- lib/properties/webkitTextEmphasisColor.js | 8 +- lib/properties/webkitTextFillColor.js | 8 +- lib/properties/webkitTextStrokeColor.js | 8 +- lib/properties/width.js | 8 +- lib/utils/propertyDescriptors.js | 64 +- package-lock.json | 2208 +++------------------ package.json | 34 +- {lib/utils => scripts}/camelize.js | 2 +- scripts/generateImplementedProperties.mjs | 42 - scripts/generateProperties.js | 5 +- scripts/generatePropertyList.mjs | 47 + test/CSSStyleDeclaration.test.js | 1556 +-------------- test/CSSStyleProperties.test.js | 1513 ++++++++++++++ test/camelize.test.js | 2 +- test/parsers.test.js | 22 +- test/properties.test.js | 6 +- 96 files changed, 2786 insertions(+), 4048 deletions(-) create mode 100644 lib/CSSStyleProperties.js create mode 100644 lib/index.js rename {lib/utils => scripts}/camelize.js (94%) delete mode 100644 scripts/generateImplementedProperties.mjs create mode 100644 scripts/generatePropertyList.mjs create mode 100644 test/CSSStyleProperties.test.js 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 b9b43235..dcf545f5 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,9 +20,6 @@ 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"); /** @@ -32,18 +27,14 @@ const { asciiLowercase } = require("./utils/strings"); */ 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 {Function} opt.onChange - Callback when cssText change or property removed */ - constructor(onChangeCallback, opt = {}) { - // Make constructor and internals non-enumerable. + constructor(globalObject = globalThis, opt = {}) { + // Make internals non-enumerable. Object.defineProperties(this, { - constructor: { - enumerable: false, - writable: true - }, - // Window _global: { value: globalThis, @@ -58,8 +49,8 @@ class CSSStyleDeclaration { writable: true }, - // CSSRule - _parentNode: { + // CSSStyleRule + _parentRule: { value: null, enumerable: false, writable: true @@ -71,6 +62,12 @@ class CSSStyleDeclaration { writable: true }, + _options: { + value: null, + enumerable: false, + writable: true + }, + _values: { value: new Map(), enumerable: false, @@ -90,7 +87,7 @@ class CSSStyleDeclaration { }, _computed: { - value: false, + value: undefined, enumerable: false, writable: true }, @@ -101,34 +98,35 @@ class CSSStyleDeclaration { writable: true }, - _setInProgress: { + _updating: { value: false, enumerable: false, writable: true } }); + this._global = globalObject; + const { context } = opt; 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; + if (context.nodeType === 1) { this._ownerNode = context; } 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; + if (typeof opt.onChange === "function") { + this._onChange = opt.onChange; + } + if (opt.format === "computedValue") { + this._computed = true; + } else { + opt.format = "specifiedValue"; + } + if (opt.readOnly) { + this._readonly = true; } + this._options = opt; } get cssText() { @@ -150,7 +148,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,10 +169,10 @@ 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 { const valueObj = parseCSS( val, @@ -206,7 +204,8 @@ class CSSStyleDeclaration { } } else { const parsedValue = parsePropertyValue(property, value, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (parsedValue) { if (properties.has(property)) { @@ -224,7 +223,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; @@ -235,7 +235,7 @@ class CSSStyleDeclaration { } catch { return; } - this._setInProgress = false; + this._updating = false; if (typeof this._onChange === "function") { this._onChange(this.cssText); } @@ -255,36 +255,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 */ @@ -304,26 +274,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) || ""; } /** @@ -350,7 +312,7 @@ class CSSStyleDeclaration { return; } const property = asciiLowercase(prop); - if (!allProperties.has(property) && !allExtraProperties.has(property)) { + if (!propertyList.has(property)) { return; } if (priority) { @@ -360,6 +322,45 @@ class CSSStyleDeclaration { } this[property] = value; } + + /** + * @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); + } + } + return prevValue; + } + + get parentRule() { + return this._parentRule; + } + + // Non-standard + setOptions(opt = {}) { + for (const [key, value] of Object.entries(opt)) { + this._options[key] = value; + if (key === "readOnly") { + this._readonly = value; + } + } + } } // Internal methods @@ -403,7 +404,7 @@ Object.defineProperties(CSSStyleDeclaration.prototype, { if ( typeof this._onChange === "function" && this.cssText !== originalText && - !this._setInProgress + !this._updating ) { this._onChange(this.cssText); } @@ -442,7 +443,8 @@ Object.defineProperties(CSSStyleDeclaration.prototype, { } } const parsedProperties = prepareBorderProperties(prop, val, prior, properties, { - globalObject: this._global + globalObject: this._global, + options: this._options }); for (const [property, item] of parsedProperties) { const { priority, value } = item; @@ -507,8 +509,10 @@ Object.defineProperties(CSSStyleDeclaration.prototype, { } } else { const parsedValue = shorthandItem.parse(longhandValues.join(" ")); - const shorthandValue = Object.values(parsedValue).join(" "); - this._setProperty(shorthandProperty, shorthandValue, priority); + if (parsedValue) { + const shorthandValue = Object.values(parsedValue).join(" "); + this._setProperty(shorthandProperty, shorthandValue, priority); + } } } } @@ -605,7 +609,7 @@ Object.defineProperties(CSSStyleDeclaration.prototype, { } if (longhandValues.length === shorthandFor.size) { const replacedValue = getPositionValue(longhandValues, shorthandPosition); - this._setProperty(shorthandProperty, replacedValue); + this._setProperty(shorthandProperty, replacedValue, priority); } } }, @@ -613,24 +617,6 @@ Object.defineProperties(CSSStyleDeclaration.prototype, { } }); -// Properties -Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties); - -// 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); - } - } -}); - 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..cd58da94 100644 --- a/lib/generated/allProperties.js +++ b/lib/generated/allProperties.js @@ -1,5 +1,5 @@ "use strict"; -// autogenerated - 2025-08-02 +// autogenerated - 2025-09-20 // 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..123d2101 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"); @@ -18,7 +18,7 @@ const padding = require("./properties/padding"); const borderImageProperty = "border-image"; -exports.shorthandProperties = new Map([ +const shorthandProperties = new Map([ ["background", background], [ "border", @@ -45,7 +45,7 @@ exports.shorthandProperties = new Map([ ["padding", padding] ]); -exports.borderProperties = new Set([ +const borderProperties = new Set([ "border", borderImageProperty, ...border.shorthandFor.keys(), @@ -56,7 +56,7 @@ exports.borderProperties = new Set([ ...borderLeft.shorthandFor.keys() ]); -exports.getPositionValue = (positionValues, position) => { +const getPositionValue = (positionValues, position) => { switch (positionValues.length) { case 1: { const [val1] = positionValues; @@ -160,9 +160,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 +172,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 }; @@ -350,11 +351,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) { @@ -404,9 +409,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,9 +418,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } if ( positionItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, { - globalObject - }) + !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, parseOpt) ) { positionItem.value = ""; } @@ -463,9 +464,7 @@ 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 = ""; } @@ -514,9 +513,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 = ""; } @@ -570,9 +571,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = if (value.length === 1) { const [propertyValue] = value; if (nameItem.value && propertyValue) { - nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, { - globalObject - }); + nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, parseOpt); } } else { nameItem.value = ""; @@ -624,9 +623,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = positionItem.value = replaceBorderShorthandValue( positionValues[position], positionItem.value, - { - globalObject - } + parseOpt ); } const longhandProperty = `${positionProperty}-${prop2}`; @@ -658,11 +655,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; } @@ -803,7 +796,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 +805,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 +815,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,7 +824,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(longhandProperty, longhandItem); + borderProps.set(longhandProperty, longhandItem); } } const borderImageItem = borderItems.get(borderImageProperty) ?? { @@ -839,8 +832,8 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(borderImageProperty, borderImageItem); - return borderProperties; + borderProps.set(borderImageProperty, borderImageItem); + return borderProps; }; const generateBorderLineShorthand = (items, property, prior) => { @@ -849,7 +842,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 }]; }; @@ -1156,18 +1149,22 @@ const prepareBorderShorthands = (properties) => { 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 +1183,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 +1199,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, @@ -1240,7 +1235,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 +1246,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 +1256,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 - }); + const lineItems = border.parse(value, parseOpt); for (const [key, initialValue] of border.initialValues) { if (!Object.hasOwn(lineItems, key)) { lineItems[key] = initialValue; @@ -1304,15 +1297,13 @@ exports.prepareProperties = (properties, opt = {}) => { }); } } 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 +1362,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 +1406,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..097114e6 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -73,7 +73,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,28 +100,24 @@ 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) => { if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val); } const ast = cssTree.parse(val, opt); if (toObject) { @@ -132,9 +128,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) => { if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val); } if (val === "") { return true; @@ -150,7 +146,7 @@ exports.isValidPropertyValue = (prop, val) => { } let ast; try { - ast = exports.parseCSS(val, { + ast = parseCSS(val, { context: "value" }); } catch { @@ -160,15 +156,19 @@ 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 = defaultOptions) => { if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val); } - if (val === "" || exports.hasVarFunc(val) || !exports.hasCalcFunc(val)) { + if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) { return val; } - const obj = exports.parseCSS(val, { context: "value" }, true); + const obj = parseCSS(val, { context: "value" }, true); if (!obj?.children) { return; } @@ -197,15 +197,13 @@ exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { }; // Parse property value. Returns string or array of parsed object. -exports.parsePropertyValue = (prop, val, opt = {}) => { - const { caseSensitive, globalObject, inArray } = opt; - val = exports.prepareValue(val, globalObject); - if (val === "" || exports.hasVarFunc(val)) { +const parsePropertyValue = (prop, val, opt = {}) => { + const { caseSensitive, globalObject, inArray, options } = opt; + 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, options); if (typeof calculatedValue !== "string") { return; } @@ -237,7 +235,7 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { return; } try { - const ast = exports.parseCSS(val, { + const ast = parseCSS(val, { context: "value" }); const { error, matched } = cssTree.lexer.matchProperty(prop, ast); @@ -285,7 +283,7 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { type: "Calc", name: "calc", isNumber: false, - value: `${asciiLowercase(itemValue)}`, + value: asciiLowercase(itemValue), raw }); } @@ -333,7 +331,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 +354,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 +376,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 +407,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 +430,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 +439,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 +450,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 +468,7 @@ exports.parseUrl = (val) => { }; // Parse . -exports.parseString = (val) => { +const parseString = (val) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "String") { @@ -481,23 +479,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 +501,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 +512,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..87c399c1 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -34,11 +34,11 @@ 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, options); } if (!parsers.isValidPropertyValue(property, v)) { return; @@ -90,7 +90,7 @@ module.exports.parse = function parse(v, opt = {}) { 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,21 +100,21 @@ 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 }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgPosition.push(parsedValue); } break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgRepeat.push(parsedValue); } @@ -124,7 +124,7 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -146,7 +146,7 @@ module.exports.parse = function parse(v, opt = {}) { 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 +156,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 +166,21 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgRepeat.push(parsedValue); } break; } case "background-size": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgSize.push(parsedValue); } break; } default: { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -195,21 +195,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 +248,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..91eb21e9 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; } @@ -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..fbf1b9a1 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; } @@ -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..e3c773ed 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; } @@ -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..0ef44e66 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; } @@ -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..a115d1e5 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; } @@ -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..deb8148a 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) { @@ -25,7 +26,7 @@ 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 val = parsers.parseLengthPercentage(parsedValue.children, options); if (val) { parsedValues.push(val); } else { @@ -52,7 +53,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..25b72e2a 100644 --- a/lib/properties/font.js +++ b/lib/properties/font.js @@ -20,11 +20,11 @@ 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, options); } if (!parsers.isValidPropertyValue(property, v)) { return; @@ -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/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..ea17a6dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,22 +9,22 @@ "version": "5.3.1", "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^4.0.3", + "@asamuzakjp/css-color": "^4.0.4", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0" }, "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.0.11", + "eslint": "^9.36.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,9 +33,9 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.4.tgz", + "integrity": "sha512-cKjSKvWGmAziQWbCouOsFwb14mp1betm8Y7Fn+yglDMUUu3r9DCbJ9iJbeFDenLMqFbIMC0pQP8K+B8LAxX3OQ==", "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.4", @@ -61,14 +61,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 +108,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 +139,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 +158,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": { @@ -314,9 +314,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,9 +371,9 @@ } }, "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.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -381,9 +381,9 @@ } }, "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.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -431,9 +431,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.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", + "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", "dev": true, "license": "MIT", "engines": { @@ -454,13 +454,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.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -600,9 +600,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.0.11", + "resolved": "https://registry.npmjs.org/@webref/css/-/css-7.0.11.tgz", + "integrity": "sha512-UDid24B3uueRSRwUNI/V3cDXIVgD0ZPk7KIVMTHGvKzmUWJbuky2pl8UJizLkN7FiwXqVMIPxkbrm6qc7EY2Hg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -672,71 +672,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 +690,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 +772,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 +797,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 +811,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.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", + "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", "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.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.36.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -1251,9 +888,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 +1091,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,258 +1101,55 @@ "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" + "node": ">=10.13.0" + } + }, + "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/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/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", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "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==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "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==", - "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", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" + "function-bind": "^1.1.2" }, "engines": { "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 +1187,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 +1203,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 +1213,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 +1226,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 +1273,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 +1321,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", @@ -2306,16 +1353,6 @@ "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 +1414,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_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==", - "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" - }, - "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": "^20.5.0 || >=22.0.0", + "npm": ">= 10" } }, - "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==", + "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", "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" + "node": ">=12" }, - "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 +1508,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 +1553,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 +1580,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 +1620,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 +1725,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 +1771,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 +1848,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 +1858,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 +1874,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..80eff99b 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "keywords": [ "CSS", "CSSStyleDeclaration", + "CSSStyleProperties", "StyleSheet" ], "version": "5.3.1", @@ -35,37 +36,38 @@ "files": [ "lib/" ], - "main": "./lib/CSSStyleDeclaration.js", + "main": "./lib/index.js", "dependencies": { - "@asamuzakjp/css-color": "^4.0.3", + "@asamuzakjp/css-color": "^4.0.4", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0" }, "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.0.11", + "eslint": "^9.36.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/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..58ba4cbe 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; @@ -27,8 +27,7 @@ const outFile = fs.createWriteStream( encoding: "utf-8" } ); -const dateToday = new Date(); -const [dateTodayFormatted] = dateToday.toISOString().split("T"); +const [dateTodayFormatted] = new Date().toISOString().split("T"); const output = `"use strict"; // autogenerated - ${dateTodayFormatted} // https://www.w3.org/Style/CSS/all-properties.en.html 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 b4441622..8baf6cba 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -2,50 +2,18 @@ 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", () => { + it("does not enumerate 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", () => { + it("has methods", () => { const style = new CSSStyleDeclaration(); - assert.strictEqual(typeof style.item, "function"); assert.strictEqual(typeof style.getPropertyValue, "function"); assert.strictEqual(typeof style.setProperty, "function"); @@ -53,34 +21,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", () => { + it("has attributes", () => { const style = new CSSStyleDeclaration(); - assert.ok(style.__lookupGetter__("cssText")); assert.ok(style.__lookupSetter__("cssText")); assert.ok(style.__lookupGetter__("length")); @@ -88,1098 +30,114 @@ describe("CSSStyleDeclaration", () => { assert.ok(style.__lookupGetter__("parentRule")); }); - it("sets internals for Window", () => { + it("sets internals for Element", () => { 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 - } + defaultView: window } }; - 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 - } - } + 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: green!"; - assert.strictEqual(style.cssText, ""); - }); - - 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: 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", () => { + const window = { + DOMException: globalThis.DOMException + }; const rule = { parentRule: {}, 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"); + assert.deepEqual(style.parentRule, rule); }); - 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"); - }); - - 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("has format in internal options", () => { + const style = new CSSStyleDeclaration(null, { + foo: "bar" }); - - 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); + assert.deepEqual(style._options, { + foo: "bar", + format: "specifiedValue" }); - } - - 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;"); + it("should not override format if exists", () => { + const style = new CSSStyleDeclaration(null, { + format: "computedValue" }); - 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;"); + assert.deepEqual(style._options, { + 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++; - }); - - 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 computedflag is set", () => { + const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException + }; + 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", () => { + it("setting improper css to cssText should not throw", () => { const style = new CSSStyleDeclaration(); 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", () => { + it("item() throws if argument is not given", () => { 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"); + 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; } - } - - 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()`", () => { + it("camelcase properties are not assigned with setproperty()", () => { const style = new CSSStyleDeclaration(); 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", () => { + it("custom properties are case-sensitive", () => { 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"); - }); + 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", () => { @@ -1203,398 +161,40 @@ describe("CSSStyleDeclaration", () => { 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("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("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", () => { + it("getPropertyPriority for property", () => { const style = new CSSStyleDeclaration(); - style.cssText = "color: red; }"; - assert.strictEqual(style.cssText, "color: red;"); + style.setProperty("color", "green", "important"); + assert.strictEqual(style.getPropertyPriority("color"), "important"); }); - it("single value flex with CSS-wide keyword", () => { + it("getPropertyPriority for custom property", () => { 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;"); + style.setProperty("--foo", "green", "important"); + assert.strictEqual(style.getPropertyPriority("--foo"), "important"); }); - 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("removeProperty throws if readonly flag is set", () => { + const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException + }; + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green"); + style.setProperty("--bar", "red"); + assert.strictEqual(style.removeProperty("--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")), - (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"); + 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..ce91ee95 --- /dev/null +++ b/test/CSSStyleProperties.test.js @@ -0,0 +1,1513 @@ +"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"); + +describe("CSSStyleProperties", () => { + it("is instanceof CSSStyleDeclaration", () => { + const style = new CSSStyleProperties(); + assert.strictEqual(style instanceof CSSStyleDeclaration, true); + }); + + it("all dashed properties are included in propertyList", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + style.cssFloat = "left"; + assert.strictEqual(style.cssFloat, "left"); + assert.strictEqual(style.float, "left"); + }); + + it("setting float should also set cssfloat", () => { + const style = new CSSStyleProperties(); + style.float = "left"; + assert.strictEqual(style.cssFloat, "left"); + assert.strictEqual(style.float, "left"); + }); + + it("from style string", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + style.color = "#1234567"; + assert.strictEqual(style.color, ""); + }); + + it("shorthand properties with embedded spaces", () => { + let style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + style.border = "green"; + assert.strictEqual(style.cssText, "border: green;"); + assert.strictEqual(style.border, "green"); + }); + + it("setting border to green", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + style.borderColor = "var(--foo)"; + assert.strictEqual(style.cssText, "border-color: var(--foo);"); + }); + + it("setting borderColor to inherit should be okay", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + style.width = "auto"; + assert.strictEqual(style.cssText, "width: auto;"); + assert.strictEqual(style.width, "auto"); + style = new CSSStyleProperties(); + style.height = "auto"; + assert.strictEqual(style.cssText, "height: auto;"); + assert.strictEqual(style.height, "auto"); + }); + + it("Shorthand serialization with just longhands", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + + 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(); + 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(); + 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(); + 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(); + 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(); + style.setProperty("fill-opacity", 0); + assert.strictEqual(style.fillOpacity, "0"); + }); + + it("onchange callback should be called when the csstext changes", () => { + const window = { + DOMException: globalThis.DOMException + }; + 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 window = { + DOMException: globalThis.DOMException + }; + 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 window = { + DOMException: globalThis.DOMException + }; + 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 window = { + DOMException: globalThis.DOMException + }; + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + style.setProperty("fontSize", "12px"); + assert.strictEqual(style.cssText, ""); + }); + + it("casing is ignored in `.setproperty()`", () => { + const style = new CSSStyleProperties(); + 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(); + style.borderSpacing = 0; + assert.strictEqual(style.cssText, "border-spacing: 0px;"); + }); + + it("float should be valid property for `.setproperty()`", () => { + const style = new CSSStyleProperties(); + style.setProperty("float", "left"); + assert.strictEqual(style.float, "left"); + assert.strictEqual(style.getPropertyValue("float"), "left"); + }); + + it("flex-shrink works", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + style.cssText = "--foo: red"; + + assert.strictEqual(style.getPropertyValue("--foo"), "red"); + }); + + it("getPropertyValue for custom properties with setProperty", () => { + const style = new CSSStyleProperties(); + style.setProperty("--bar", "blue"); + + assert.strictEqual(style.getPropertyValue("--bar"), "blue"); + }); + + it("getPropertyValue for custom properties with object setter", () => { + const style = new CSSStyleProperties(); + style["--baz"] = "yellow"; + + assert.strictEqual(style.getPropertyValue("--baz"), ""); + }); + + it("custom properties are case-sensitive", () => { + const style = new CSSStyleProperties(); + 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(); + style.setProperty(property, "calc(100% - 100px)"); + assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); + }); + } + + it("supports nested calc", () => { + const style = new CSSStyleProperties(); + style.setProperty("width", "calc(100% - calc(200px - 100px))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); + }); + + it("supports nested calc", () => { + const style = new CSSStyleProperties(); + style.setProperty("width", "calc(100% * calc(2 / 3))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); + }); + + it("supports var", () => { + const style = new CSSStyleProperties(); + style.setProperty("width", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); + }); + + it("supports var with fallback", () => { + const style = new CSSStyleProperties(); + style.setProperty("width", "var(--foo, 100px)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); + }); + + it("supports var with var fallback", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + style.setProperty("color", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); + }); + + it("should not normalize if var() is included", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + style.setProperty("line-height", "calc(abs(1) + abs(2))"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); + }); + + it("supports sign", () => { + const style = new CSSStyleProperties(); + style.setProperty("line-height", "sign(.1)"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); + }); + + it("supports sign inside calc", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + style.cssText = "border-width: 1px;"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(); + 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(); + style.cssText = "border: none;"; + assert.strictEqual(style.cssText, "border: medium;"); + }); + + it("invalid cssText should be parsed", () => { + const style = new CSSStyleProperties(); + style.cssText = "color: red; }"; + assert.strictEqual(style.cssText, "color: red;"); + }); + + it("single value flex with CSS-wide keyword", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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" + }); + 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(); + style.setProperty("--foo", true); + assert.strictEqual(style.getPropertyValue("--foo"), "true"); + }); + + it("throws for setting Symbol", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support `-webkit-` prefixed family name", () => { + const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + style.backgroundAttachment = "var(--foo)"; + assert.strictEqual(style.backgroundAttachment, "var(--foo)"); + style.setProperty("background-attachment", "var(--bar)"); + assert.strictEqual(style.backgroundAttachment, "var(--bar)"); + }); +}); 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..ebea4bad 100644 --- a/test/parsers.test.js +++ b/test/parsers.test.js @@ -979,17 +979,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 +1001,7 @@ describe("parseUrl", () => { value: "foo" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); @@ -1013,7 +1013,7 @@ describe("parseUrl", () => { value: "" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("")'); }); @@ -1025,7 +1025,7 @@ describe("parseUrl", () => { value: "sample.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample.png")'); }); @@ -1037,7 +1037,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 +1049,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 +1061,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 +1073,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 +1086,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")'); }); diff --git a/test/properties.test.js b/test/properties.test.js index 4594780b..e4b87f83 100644 --- a/test/properties.test.js +++ b/test/properties.test.js @@ -2,10 +2,10 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); -const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); +const { CSSStyleProperties } = require("../lib/CSSStyleProperties"); function testPropertyValue(property, value, expected) { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleProperties(); let res; style.setProperty(property, value); @@ -34,7 +34,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); From 670a7650d7b4fb8dc4b55a7367c468d24e3c347e Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Wed, 24 Sep 2025 06:58:16 +0900 Subject: [PATCH 02/18] Add lru-cache --- lib/CSSStyleDeclaration.js | 9 +- lib/generated/allProperties.js | 2 +- lib/normalize.js | 15 +++- lib/parsers.js | 62 +++++++++++--- lib/properties/background.js | 8 +- lib/properties/border.js | 6 +- lib/properties/borderBottom.js | 6 +- lib/properties/borderLeft.js | 6 +- lib/properties/borderRight.js | 6 +- lib/properties/borderTop.js | 6 +- lib/properties/clip.js | 9 +- lib/properties/font.js | 4 +- lib/utils/cache.js | 123 +++++++++++++++++++++++++++ package-lock.json | 43 +++++----- package.json | 7 +- scripts/downloadLatestProperties.mjs | 14 +-- test/parsers.test.js | 18 ++-- 17 files changed, 263 insertions(+), 81 deletions(-) create mode 100644 lib/utils/cache.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index dcf545f5..29fadeab 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -174,11 +174,15 @@ class CSSStyleDeclaration { } this._updating = true; try { + // TBD: use cache? const valueObj = parseCSS( val, { - context: "declarationList", - parseValue: false + globalObject: this._global, + options: { + context: "declarationList", + parseValue: false + } }, true ); @@ -203,6 +207,7 @@ class CSSStyleDeclaration { properties.set(property, { property, value, priority }); } } else { + // TBD: use cache? const parsedValue = parsePropertyValue(property, value, { globalObject: this._global, options: this._options diff --git a/lib/generated/allProperties.js b/lib/generated/allProperties.js index cd58da94..ac7b6aba 100644 --- a/lib/generated/allProperties.js +++ b/lib/generated/allProperties.js @@ -1,5 +1,5 @@ "use strict"; -// autogenerated - 2025-09-20 +// autogenerated - 2025-09-23 // https://www.w3.org/Style/CSS/all-properties.en.html module.exports = new Set([ diff --git a/lib/normalize.js b/lib/normalize.js index 123d2101..65e95015 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -468,13 +468,22 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} ) { 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; } } diff --git a/lib/parsers.js b/lib/parsers.js index 097114e6..e66d037a 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 { createCacheKey, getCache, setCache } = require("./utils/cache"); const { asciiLowercase } = require("./utils/strings"); // CSS global keywords @@ -115,11 +116,12 @@ const hasCalcFunc = (val) => { }; // Parse CSS to AST. -const parseCSS = (val, opt, toObject = false) => { +const parseCSS = (val, opt = {}, toObject = false) => { + const { globalObject, options } = opt; if (typeof val !== "string") { - val = prepareValue(val); + val = prepareValue(val, globalObject); } - const ast = cssTree.parse(val, opt); + const ast = cssTree.parse(val, options); if (toObject) { return cssTree.toPlainObject(ast); } @@ -128,9 +130,9 @@ const parseCSS = (val, opt, toObject = false) => { // Value is a valid property value. // Returns `false` for custom property and/or var(). -const isValidPropertyValue = (prop, val) => { +const isValidPropertyValue = (prop, val, globalObject) => { if (typeof val !== "string") { - val = prepareValue(val); + val = prepareValue(val, globalObject); } if (val === "") { return true; @@ -146,8 +148,12 @@ const isValidPropertyValue = (prop, val) => { } let ast; try { + // TBD: use cache? ast = parseCSS(val, { - context: "value" + globalObject, + options: { + context: "value" + } }); } catch { return false; @@ -161,14 +167,25 @@ const defaultOptions = { }; // Simplify / resolve math functions. -const resolveCalc = (val, opt = defaultOptions) => { +const resolveCalc = (val, opt = {}) => { + const { globalObject, options } = opt; if (typeof val !== "string") { - val = prepareValue(val); + val = prepareValue(val, globalObject); } if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) { return val; } - const obj = parseCSS(val, { context: "value" }, true); + // TBD: use cache? + const obj = parseCSS( + val, + { + globalObject, + options: { + context: "value" + } + }, + true + ); if (!obj?.children) { return; } @@ -182,7 +199,7 @@ const resolveCalc = (val, opt = defaultOptions) => { .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); @@ -198,12 +215,12 @@ const resolveCalc = (val, opt = defaultOptions) => { // Parse property value. Returns string or array of parsed object. const parsePropertyValue = (prop, val, opt = {}) => { - const { caseSensitive, globalObject, inArray, options } = opt; + const { caseSensitive, globalObject, inArray } = opt; val = prepareValue(val, globalObject); if (val === "" || hasVarFunc(val)) { return val; } else if (hasCalcFunc(val)) { - const calculatedValue = resolveCalc(val, options); + const calculatedValue = resolveCalc(val, opt); if (typeof calculatedValue !== "string") { return; } @@ -235,8 +252,24 @@ const parsePropertyValue = (prop, val, opt = {}) => { return; } try { + let cacheKey = ""; + if (inArray) { + cacheKey = createCacheKey({ + name: "parsePropertyValue", + property: prop, + value: val, + caseSensitive + }); + const cachedValues = getCache(cacheKey); + if (Array.isArray(cachedValues)) { + return cachedValues; + } + } const ast = parseCSS(val, { - context: "value" + globalObject, + options: { + context: "value" + } }); const { error, matched } = cssTree.lexer.matchProperty(prop, ast); if (error || !matched) { @@ -322,6 +355,9 @@ const parsePropertyValue = (prop, val, opt = {}) => { } } } + if (cacheKey) { + setCache(cacheKey, parsedValues); + } return parsedValues; } } catch { diff --git a/lib/properties/background.js b/lib/properties/background.js index 87c399c1..06015ba4 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -38,9 +38,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { - v = parsers.resolveCalc(v, options); + v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v)) { + if (!parsers.isValidPropertyValue(property, v, globalObject)) { return; } const values = parsers.splitValue(v, { @@ -85,7 +85,7 @@ 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": @@ -141,7 +141,7 @@ 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": diff --git a/lib/properties/border.js b/lib/properties/border.js index 91eb21e9..a0b6078e 100644 --- a/lib/properties/border.js +++ b/lib/properties/border.js @@ -97,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; } diff --git a/lib/properties/borderBottom.js b/lib/properties/borderBottom.js index fbf1b9a1..3b79ec32 100644 --- a/lib/properties/borderBottom.js +++ b/lib/properties/borderBottom.js @@ -87,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; } diff --git a/lib/properties/borderLeft.js b/lib/properties/borderLeft.js index e3c773ed..1df59aa1 100644 --- a/lib/properties/borderLeft.js +++ b/lib/properties/borderLeft.js @@ -87,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; } diff --git a/lib/properties/borderRight.js b/lib/properties/borderRight.js index 0ef44e66..2ecfeb14 100644 --- a/lib/properties/borderRight.js +++ b/lib/properties/borderRight.js @@ -87,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; } diff --git a/lib/properties/borderTop.js b/lib/properties/borderTop.js index a115d1e5..38ed4267 100644 --- a/lib/properties/borderTop.js +++ b/lib/properties/borderTop.js @@ -87,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; } diff --git a/lib/properties/clip.js b/lib/properties/clip.js index deb8148a..afcb59f2 100644 --- a/lib/properties/clip.js +++ b/lib/properties/clip.js @@ -25,7 +25,14 @@ module.exports.parse = function parse(v, opt = {}) { }); const parsedValues = []; for (const item of values) { - const parsedValue = parsers.parseCSS(item, { context: "value" }, true); + const parsedValue = parsers.parseCSS( + item, + { + globalObject, + options: { context: "value" } + }, + true + ); const val = parsers.parseLengthPercentage(parsedValue.children, options); if (val) { parsedValues.push(val); diff --git a/lib/properties/font.js b/lib/properties/font.js index 25b72e2a..804832f1 100644 --- a/lib/properties/font.js +++ b/lib/properties/font.js @@ -24,9 +24,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { - v = parsers.resolveCalc(v, options); + v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v)) { + if (!parsers.isValidPropertyValue(property, v, globalObject)) { return; } const [fontBlock, ...families] = parsers.splitValue(v, { diff --git a/lib/utils/cache.js b/lib/utils/cache.js new file mode 100644 index 00000000..9d80a4d6 --- /dev/null +++ b/lib/utils/cache.js @@ -0,0 +1,123 @@ +// Forked from https://github.com/asamuzaK/cssColor/blob/main/src/js/cache.ts + +"use strict"; + +const { LRUCache } = require("lru-cache"); + +class CacheItem { + #isNull; + #item; + + constructor(item, isNull = false) { + this.#item = item; + this.#isNull = Boolean(isNull); + } + + get item() { + return this.#item; + } + + get isNull() { + return this.#isNull; + } +} + +class NullObject extends CacheItem { + constructor() { + super(Symbol("null"), true); + } +} + +// lru cache +const lruCache = new LRUCache({ + max: 4096 +}); + +/** + * @param key - cache key + * @param value - value to cache + * @returns void + */ +const setCache = (key, value) => { + if (key) { + if (value === null) { + lruCache.set(key, new NullObject()); + } else if (value instanceof CacheItem) { + lruCache.set(key, value); + } else { + lruCache.set(key, new CacheItem(value)); + } + } +}; + +/** + * @param key - cache key + * @returns cached item or false otherwise + */ +const getCache = (key) => { + if (key && lruCache.has(key)) { + const cachedItem = lruCache.get(key); + if (cachedItem instanceof CacheItem) { + if (cachedItem.isNull) { + return null; + } + return cachedItem.item; + } + // delete unexpected cached item + lruCache.delete(key); + return false; + } + return false; +}; + +/** + * @param value - CSS value + * @returns stringified value in JSON notation + */ +const valueToJsonString = (value) => { + if (typeof value === "undefined") { + return ""; + } + const res = JSON.stringify(value, (_key, val) => { + let replacedValue; + if (typeof val === "undefined") { + replacedValue = null; + } else if (typeof val === "function") { + replacedValue = val.name; + } else if (val instanceof Map || val instanceof Set) { + replacedValue = [...val]; + } else if (typeof val === "bigint") { + replacedValue = val.toString(); + } else { + replacedValue = val; + } + return replacedValue; + }); + return res; +}; + +/** + * @param keyData - key data + * @param [opt] - options + * @returns cache key + */ +const createCacheKey = (keyData, opt = {}) => { + const { customProperty = {}, dimension = {} } = opt; + let cacheKey = ""; + if ( + keyData && + Object.keys(keyData).length && + typeof customProperty.callback !== "function" && + typeof dimension.callback !== "function" + ) { + keyData.opt = valueToJsonString(opt); + cacheKey = valueToJsonString(keyData); + } + return cacheKey; +}; + +module.exports = { + createCacheKey, + getCache, + setCache +}; diff --git a/package-lock.json b/package-lock.json index ea17a6dd..4de280e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,10 @@ "version": "5.3.1", "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^4.0.4", + "@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.1" }, "devDependencies": { "@babel/generator": "^7.28.3", @@ -19,7 +20,7 @@ "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^7.0.11", + "@webref/css": "^7.1.0", "eslint": "^9.36.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", @@ -33,16 +34,16 @@ } }, "node_modules/@asamuzakjp/css-color": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.4.tgz", - "integrity": "sha512-cKjSKvWGmAziQWbCouOsFwb14mp1betm8Y7Fn+yglDMUUu3r9DCbJ9iJbeFDenLMqFbIMC0pQP8K+B8LAxX3OQ==", + "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": { @@ -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": { @@ -600,9 +601,9 @@ "license": "MIT" }, "node_modules/@webref/css": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@webref/css/-/css-7.0.11.tgz", - "integrity": "sha512-UDid24B3uueRSRwUNI/V3cDXIVgD0ZPk7KIVMTHGvKzmUWJbuky2pl8UJizLkN7FiwXqVMIPxkbrm6qc7EY2Hg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@webref/css/-/css-7.1.0.tgz", + "integrity": "sha512-a9XGQw4pYadCAIwRnREpy/+ZEL5E1oi/EARdTUK3evNGYJbec2MSpHeWorIBbtfITbDD0HPdSIatFMFQCPgJJg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1345,9 +1346,9 @@ "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.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", + "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", "license": "ISC", "engines": { "node": "20 || >=22" diff --git a/package.json b/package.json index 80eff99b..ecc7aad3 100644 --- a/package.json +++ b/package.json @@ -38,9 +38,10 @@ ], "main": "./lib/index.js", "dependencies": { - "@asamuzakjp/css-color": "^4.0.4", + "@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.1" }, "devDependencies": { "@babel/generator": "^7.28.3", @@ -48,7 +49,7 @@ "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^7.0.11", + "@webref/css": "^7.1.0", "eslint": "^9.36.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", diff --git a/scripts/downloadLatestProperties.mjs b/scripts/downloadLatestProperties.mjs index 28fa2edf..384187ba 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], null, 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/test/parsers.test.js b/test/parsers.test.js index ebea4bad..62018f90 100644 --- a/test/parsers.test.js +++ b/test/parsers.test.js @@ -1389,8 +1389,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 +1402,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 +1415,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 [ From 30e0f0bfdc5398a57911f03871510a2b8ba24946 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Fri, 3 Oct 2025 07:55:15 +0900 Subject: [PATCH 03/18] Add whitespace after comma. Workaround for https://github.com/jsdom/jsdom/issues/3939 --- lib/parsers.js | 1 + test/parsers.test.js | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/parsers.js b/lib/parsers.js index e66d037a..f4d8630c 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -197,6 +197,7 @@ const resolveCalc = (val, opt = {}) => { const value = cssTree .generate(item) .replace(/\)(?!\)|\s|,)/g, ") ") + .replace(/,(?!\s)/g, ", ") .trim(); if (calcNameRegEx.test(itemName)) { const newValue = cssCalc(value, options ?? defaultOptions); diff --git a/test/parsers.test.js b/test/parsers.test.js index 62018f90..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", () => { From 72a43faf6212b083e883e7e01631401271b7f88b Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Fri, 3 Oct 2025 09:11:58 +0900 Subject: [PATCH 04/18] Remove var() shorthand if longhand is updated Fix https://github.com/jsdom/cssstyle/issues/247 --- lib/normalize.js | 244 +++++++++++++++++--------------- test/CSSStyleProperties.test.js | 9 ++ 2 files changed, 137 insertions(+), 116 deletions(-) diff --git a/lib/normalize.js b/lib/normalize.js index 65e95015..df2a2f98 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -16,19 +16,28 @@ const font = require("./properties/font"); const margin = require("./properties/margin"); const padding = require("./properties/padding"); -const borderImageProperty = "border-image"; +const BORDER = "border"; +const BORDER_IMAGE = "border-image"; +const TOP = "top"; +const RIGHT = "right"; +const BOTTOM = "bottom"; +const LEFT = "left"; +const WIDTH = "width"; +const STYLE = "style"; +const COLOR = "color"; +const NONE = "none"; 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] ]) } ], @@ -46,8 +55,8 @@ const shorthandProperties = new Map([ ]); const borderProperties = new Set([ - "border", - borderImageProperty, + BORDER, + BORDER_IMAGE, ...border.shorthandFor.keys(), ...border.positionShorthandFor.keys(), ...borderTop.shorthandFor.keys(), @@ -65,16 +74,16 @@ const 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 +97,16 @@ const 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 +123,16 @@ const 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 +154,9 @@ const 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) => { @@ -221,16 +230,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: @@ -243,25 +252,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(" "); } @@ -277,7 +286,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) { @@ -285,13 +294,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) { @@ -299,7 +308,7 @@ const replacePositionValue = (value, positionValues, position) => { } return [val1, val2, value].join(" "); } - case "left": { + case LEFT: { if (val2 === value) { return positionValues.join(" "); } @@ -315,13 +324,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) { @@ -329,13 +338,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) { @@ -382,7 +391,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} // 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}`; @@ -424,14 +433,14 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} } } 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 } 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`; @@ -495,7 +504,7 @@ const 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); @@ -503,7 +512,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} // border-width, border-style, border-color } 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; @@ -536,14 +545,14 @@ const 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 } 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); @@ -566,7 +575,7 @@ const 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)) { @@ -574,13 +583,17 @@ const 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, parseOpt); + if (nameItem.value) { + if (hasVarFunc(nameItem.value)) { + nameItem.value = ""; + } else if (propertyValue) { + nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, parseOpt); + } } } else { nameItem.value = ""; @@ -643,7 +656,7 @@ const 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") { @@ -653,7 +666,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} 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`; @@ -677,7 +690,7 @@ const 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, @@ -685,7 +698,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} prop2 ); } - } else if (line === "style") { + } else if (line === STYLE) { if (lineStyleItem.value) { lineStyleItem.value = replacePositionValue( itemValue, @@ -693,7 +706,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} prop2 ); } - } else if (line === "color") { + } else if (line === COLOR) { if (lineColorItem.value) { lineColorItem.value = replacePositionValue( itemValue, @@ -706,7 +719,7 @@ const 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, @@ -714,7 +727,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} prop2 ); } - } else if (line === "style") { + } else if (line === STYLE) { if (lineStyleItem.value) { lineStyleItem.value = replacePositionValue( itemValue, @@ -722,7 +735,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} prop2 ); } - } else if (line === "color") { + } else if (line === COLOR) { if (lineColorItem.value) { lineColorItem.value = replacePositionValue( itemValue, @@ -737,7 +750,7 @@ const 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); @@ -745,7 +758,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} // 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`; @@ -755,7 +768,7 @@ const 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 { @@ -794,7 +807,7 @@ const 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); @@ -836,12 +849,12 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} borderProps.set(longhandProperty, longhandItem); } } - const borderImageItem = borderItems.get(borderImageProperty) ?? { - property: borderImageProperty, + const borderImageItem = borderItems.get(BORDER_IMAGE) ?? { + property: BORDER_IMAGE, value: "", priority: "" }; - borderProps.set(borderImageProperty, borderImageItem); + borderProps.set(BORDER_IMAGE, borderImageItem); return borderProps; }; @@ -894,7 +907,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 { @@ -902,7 +915,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "style": { + case STYLE: { if (priority) { lineStylePriorItems.set(property, { property, value, priority }); } else { @@ -910,7 +923,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "color": { + case COLOR: { if (priority) { lineColorPriorItems.set(property, { property, value, priority }); } else { @@ -921,7 +934,7 @@ const prepareBorderShorthands = (properties) => { default: } switch (positionPart) { - case "top": { + case TOP: { if (priority) { positionTopPriorItems.set(property, { property, value, priority }); } else { @@ -929,7 +942,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "right": { + case RIGHT: { if (priority) { positionRightPriorItems.set(property, { property, value, priority }); } else { @@ -937,7 +950,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "bottom": { + case BOTTOM: { if (priority) { positionBottomPriorItems.set(property, { property, value, priority }); } else { @@ -945,7 +958,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "left": { + case LEFT: { if (priority) { positionLeftPriorItems.set(property, { property, value, priority }); } else { @@ -998,9 +1011,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1011,9 +1024,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1025,9 +1038,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1038,9 +1051,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1052,9 +1065,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1065,9 +1078,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1079,9 +1092,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1092,9 +1105,9 @@ const prepareBorderShorthands = (properties) => { 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); } @@ -1103,33 +1116,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); } } } @@ -1151,9 +1163,9 @@ 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; }; @@ -1270,7 +1282,7 @@ const prepareProperties = (properties, opt = {}) => { for (const [property, item] of borderProps) { if (shorthandProperties.has(property)) { const { value, priority } = item; - if (property === "border") { + if (property === BORDER) { const lineItems = border.parse(value, parseOpt); for (const [key, initialValue] of border.initialValues) { if (!Object.hasOwn(lineItems, key)) { @@ -1299,9 +1311,9 @@ const prepareProperties = (properties, opt = {}) => { } } if (value) { - longhandProperties.set(borderImageProperty, { - property: borderImageProperty, - value: "none", + longhandProperties.set(BORDER_IMAGE, { + property: BORDER_IMAGE, + value: NONE, priority }); } diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js index ce91ee95..f7888ce9 100644 --- a/test/CSSStyleProperties.test.js +++ b/test/CSSStyleProperties.test.js @@ -1510,4 +1510,13 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () 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(); + 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, ""); + }); }); From 7b7030d4f8298009b91e0d24580d4aae4c02c715 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 4 Oct 2025 05:48:38 +0900 Subject: [PATCH 05/18] Update CSSStyleProperties.test.js Unit test for https://github.com/jsdom/jsdom/issues/3940 --- test/CSSStyleProperties.test.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js index f7888ce9..aaf40c2e 100644 --- a/test/CSSStyleProperties.test.js +++ b/test/CSSStyleProperties.test.js @@ -1519,4 +1519,12 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () assert.strictEqual(style.borderWidth, "0.2rem"); assert.strictEqual(style.border, ""); }); + + it("should get value and priority", () => { + const style = new CSSStyleProperties(); + 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"); + }); }); From 734350b4bea2c83696d965e622ec5d6f0b099897 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 4 Oct 2025 08:33:43 +0900 Subject: [PATCH 06/18] Update normalize.js --- lib/normalize.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/normalize.js b/lib/normalize.js index df2a2f98..341a9049 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -388,7 +388,7 @@ const 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(BORDER_IMAGE, properties); @@ -437,7 +437,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} 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(BORDER_IMAGE, properties); @@ -509,7 +509,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} 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(BORDER_IMAGE, properties); @@ -547,7 +547,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} borderItems.set(nameProperty, nameItem); borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); - // border shorthand + // Handle border shorthand } else { const nameItem = getPropertyItem(nameProperty, properties); const imageItem = getPropertyItem(BORDER_IMAGE, properties); @@ -660,7 +660,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} 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; @@ -755,7 +755,7 @@ const prepareBorderProperties = (property, value, priority, properties, opt = {} 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(BORDER_IMAGE, properties); From ef80b221a86bbb6fb1e1dbc42ff0d587d77aa625 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 4 Oct 2025 08:34:54 +0900 Subject: [PATCH 07/18] Update dependencies --- package-lock.json | 61 +++++++++++++++++++++++++---------------------- package.json | 6 ++--- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4de280e2..236f4742 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@asamuzakjp/css-color": "^4.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0", - "lru-cache": "^11.2.1" + "lru-cache": "^11.2.2" }, "devDependencies": { "@babel/generator": "^7.28.3", @@ -20,8 +20,8 @@ "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^7.1.0", - "eslint": "^9.36.0", + "@webref/css": "^7.1.1", + "eslint": "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "globals": "^16.4.0", @@ -372,19 +372,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "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.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "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": { @@ -432,9 +435,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", - "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "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": { @@ -455,13 +458,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "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.2", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { @@ -601,9 +604,9 @@ "license": "MIT" }, "node_modules/@webref/css": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@webref/css/-/css-7.1.0.tgz", - "integrity": "sha512-a9XGQw4pYadCAIwRnREpy/+ZEL5E1oi/EARdTUK3evNGYJbec2MSpHeWorIBbtfITbDD0HPdSIatFMFQCPgJJg==", + "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": { @@ -812,20 +815,20 @@ } }, "node_modules/eslint": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", - "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "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.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.36.0", - "@eslint/plugin-kit": "^0.3.5", + "@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", @@ -1346,9 +1349,9 @@ "license": "MIT" }, "node_modules/lru-cache": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz", - "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==", + "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" diff --git a/package.json b/package.json index ecc7aad3..560b9a3a 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@asamuzakjp/css-color": "^4.0.5", "@csstools/css-syntax-patches-for-csstree": "^1.0.14", "css-tree": "^3.1.0", - "lru-cache": "^11.2.1" + "lru-cache": "^11.2.2" }, "devDependencies": { "@babel/generator": "^7.28.3", @@ -49,8 +49,8 @@ "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^7.1.0", - "eslint": "^9.36.0", + "@webref/css": "^7.1.1", + "eslint": "^9.37.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "globals": "^16.4.0", From 62ae98923b180d48e8d2330459bbfe2a3d237d7c Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sun, 5 Oct 2025 08:14:36 +0900 Subject: [PATCH 08/18] Update build.yml --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From b78c3d4c872dd166115eec831ecf2d832f9b0adf Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sun, 12 Oct 2025 13:38:34 +0900 Subject: [PATCH 09/18] correct and update the points that were reviewed --- lib/CSSStyleDeclaration.js | 546 ++++++++++++--------------- lib/utils/cache.js | 33 +- scripts/downloadLatestProperties.mjs | 2 +- scripts/generateProperties.js | 3 +- test/CSSStyleDeclaration.test.js | 7 - test/CSSStyleProperties.test.js | 3 - 6 files changed, 257 insertions(+), 337 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 29fadeab..20174465 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -30,84 +30,15 @@ class CSSStyleDeclaration { * @param {object} globalObject - Window * @param {object} opt - Options * @param {object} opt.context - Element or CSSStyleRule - * @param {Function} opt.onChange - Callback when cssText change or property removed + * @param {Function} opt.onChange - Callback when cssText is changed or the property is removed + * @param {boolean} opt.computed - The computed flag + * @param {boolean} opt.readOnly - The read-only flag */ constructor(globalObject = globalThis, opt = {}) { - // Make internals non-enumerable. - Object.defineProperties(this, { - // Window - _global: { - value: globalThis, - enumerable: false, - writable: true - }, - - // Element - _ownerNode: { - value: null, - enumerable: false, - writable: true - }, - - // CSSStyleRule - _parentRule: { - value: null, - enumerable: false, - writable: true - }, - - _onChange: { - value: null, - enumerable: false, - writable: true - }, - - _options: { - 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: undefined, - enumerable: false, - writable: true - }, - - _readonly: { - value: false, - enumerable: false, - writable: true - }, - - _updating: { - value: false, - enumerable: false, - writable: true - } - }); - - this._global = globalObject; - const { context } = opt; + this._global = globalObject; + this._ownerNode = null; + this._parentRule = null; if (context) { if (context.nodeType === 1) { this._ownerNode = context; @@ -115,18 +46,20 @@ class CSSStyleDeclaration { this._parentRule = context; } } - if (typeof opt.onChange === "function") { - this._onChange = opt.onChange; - } + this._onChange = opt.onChange; + this._readonly = Boolean(opt.readOnly); + this._computed = undefined; if (opt.format === "computedValue") { this._computed = true; } else { + // Set the default format value. opt.format = "specifiedValue"; } - if (opt.readOnly) { - this._readonly = true; - } this._options = opt; + this._values = new Map(); + this._priorities = new Map(); + this._length = 0; + this._updating = false; } get cssText() { @@ -251,8 +184,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]; @@ -357,270 +289,258 @@ class CSSStyleDeclaration { return this._parentRule; } - // Non-standard + /** + * Non-standard. + * Use this method to set the computed and read-only flags after the value of getComputedStyle() + * has been resolved. + * @param {object} opt - Options + * @returns {void} + */ setOptions(opt = {}) { for (const [key, value] of Object.entries(opt)) { - this._options[key] = value; - if (key === "readOnly") { + if (key === "computed") { + opt[key] = value; + this._computed = value; + } else if (key === "readOnly") { + opt[key] = value; this._readonly = 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 = ""; - 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. + // 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++; } - 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); - } - }, - enumerable: false - }, - - _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, - options: this._options - }); - for (const [property, item] of parsedProperties) { - const { priority, value } = item; - this._setProperty(property, value, priority); - } - }, - enumerable: false - }, + } 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); + } + } - _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); + /** + * @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) ?? ""; } - 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); - } - } - 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); - } + 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 }); } } - }, - 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); + } + } - _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; - } - 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); + /** + * @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 (longhandPriority) { - hasPriority = true; - } else { - this._setProperty(longhandProperty, longhandValue, priority); + if (!longhandValue || longhandPriority !== priority) { + break; + } + if (isGlobalKeyword(longhandValue)) { + hasGlobalKeyword = true; } + longhandValues.push(longhandValue); } } - if (hasPriority) { - this.removeProperty(prop); - } else { - const shorthandValue = getPositionValue(shorthandValues, position); - this._setProperty(prop, shorthandValue, priority); + 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 - }, + } + } - _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; + /** + * @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 { - priority = this._priorities.get(prop) ?? ""; + const longhandPriority = this._priorities.get(longhandProperty) ?? ""; + if (longhandPriority) { + hasPriority = true; + } else { + this._setProperty(longhandProperty, longhandValue, priority); + } } + } + if (hasPriority) { 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); + } else { + const shorthandValue = getPositionValue(shorthandValues, position); + this._setProperty(prop, shorthandValue, priority); + } + } + + /** + * @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); } - }, - enumerable: false + } } -}); +} module.exports = { CSSStyleDeclaration diff --git a/lib/utils/cache.js b/lib/utils/cache.js index 9d80a4d6..f9a46f52 100644 --- a/lib/utils/cache.js +++ b/lib/utils/cache.js @@ -1,4 +1,10 @@ // Forked from https://github.com/asamuzaK/cssColor/blob/main/src/js/cache.ts +// cssstyle calculates values using associated options. +// Even for the same property, the calculated result will differ depending on the option values. +// For example, if a keyData is `{ border-color: currentColor }` and the option for that keyData +// is `{ currentColor: red }`, then if the keyData is the same `{ border-color: currentColor }` +// but the option is `{ currentColor: green }`, the calculated result will be different. +// For this reason, we combine keyData and options to increase the uniqueness of cache keywords. "use strict"; @@ -22,21 +28,22 @@ class CacheItem { } } +// The lru-cache requires values to be non-null, so we provide a NullObject for null values. class NullObject extends CacheItem { constructor() { super(Symbol("null"), true); } } -// lru cache +// lru-cache instance. const lruCache = new LRUCache({ max: 4096 }); /** - * @param key - cache key - * @param value - value to cache - * @returns void + * @param key - Cache key. + * @param value - Value to cache. + * @returns void. */ const setCache = (key, value) => { if (key) { @@ -51,8 +58,8 @@ const setCache = (key, value) => { }; /** - * @param key - cache key - * @returns cached item or false otherwise + * @param key - Cache key. + * @returns Cached item or false otherwise. */ const getCache = (key) => { if (key && lruCache.has(key)) { @@ -63,7 +70,7 @@ const getCache = (key) => { } return cachedItem.item; } - // delete unexpected cached item + // Delete unexpected cached item. lruCache.delete(key); return false; } @@ -71,8 +78,8 @@ const getCache = (key) => { }; /** - * @param value - CSS value - * @returns stringified value in JSON notation + * @param value - CSS value. + * @returns Stringified value in JSON notation. */ const valueToJsonString = (value) => { if (typeof value === "undefined") { @@ -97,9 +104,11 @@ const valueToJsonString = (value) => { }; /** - * @param keyData - key data - * @param [opt] - options - * @returns cache key + * Create stringified cacheKey from keyData and options. + * Note that if the options include a function, it will not be cached. + * @param keyData - Key data object. + * @param [opt] - Options. + * @returns Cache key. */ const createCacheKey = (keyData, opt = {}) => { const { customProperty = {}, dimension = {} } = opt; diff --git a/scripts/downloadLatestProperties.mjs b/scripts/downloadLatestProperties.mjs index 384187ba..ad3beae3 100644 --- a/scripts/downloadLatestProperties.mjs +++ b/scripts/downloadLatestProperties.mjs @@ -29,7 +29,7 @@ for (const cssProp of rawCSSProperties) { propertyNames.add(cssProp.property); } -const propertyNamesJSON = JSON.stringify([...propertyNames], null, 2); +const propertyNamesJSON = JSON.stringify([...propertyNames], undefined, 2); const dateToday = new Date(); const [dateTodayFormatted] = dateToday.toISOString().split("T"); const output = `"use strict"; diff --git a/scripts/generateProperties.js b/scripts/generateProperties.js index 58ba4cbe..30372f2d 100644 --- a/scripts/generateProperties.js +++ b/scripts/generateProperties.js @@ -27,7 +27,8 @@ const outFile = fs.createWriteStream( encoding: "utf-8" } ); -const [dateTodayFormatted] = new Date().toISOString().split("T"); +const dateToday = new Date(); +const [dateTodayFormatted] = dateToday.toISOString().split("T"); const output = `"use strict"; // autogenerated - ${dateTodayFormatted} // https://www.w3.org/Style/CSS/all-properties.en.html diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 8baf6cba..885a1941 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -5,13 +5,6 @@ const assert = require("node:assert/strict"); const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); describe("CSSStyleDeclaration", () => { - it("does not enumerate internals", () => { - const style = new CSSStyleDeclaration(); - for (const i in style) { - assert.strictEqual(i.startsWith("_"), false); - } - }); - it("has methods", () => { const style = new CSSStyleDeclaration(); assert.strictEqual(typeof style.item, "function"); diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js index aaf40c2e..c5600283 100644 --- a/test/CSSStyleProperties.test.js +++ b/test/CSSStyleProperties.test.js @@ -1351,9 +1351,6 @@ describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => 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" - }); assert.strictEqual(style.getPropertyValue("--foo"), "1"); }); }); From 0a8ea69ac44fedd6ef03d151a88290c90a649e38 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 13 Oct 2025 07:58:07 +0900 Subject: [PATCH 10/18] Rename method --- lib/CSSStyleDeclaration.js | 16 +++------------- test/CSSStyleDeclaration.test.js | 29 ++++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 20174465..f9a1bd8e 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -291,21 +291,11 @@ class CSSStyleDeclaration { /** * Non-standard. - * Use this method to set the computed and read-only flags after the value of getComputedStyle() - * has been resolved. - * @param {object} opt - Options + * Use this method to set the read-only flag after getComputedStyle() has been resolved. * @returns {void} */ - setOptions(opt = {}) { - for (const [key, value] of Object.entries(opt)) { - if (key === "computed") { - opt[key] = value; - this._computed = value; - } else if (key === "readOnly") { - opt[key] = value; - this._readonly = value; - } - } + setReadOnlyFlag() { + this._readonly = true; } // Internal methods diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 885a1941..4b5d42ca 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -85,7 +85,7 @@ describe("CSSStyleDeclaration", () => { }); }); - it("getting cssText returns empty string if computedflag is set", () => { + it("getting cssText returns empty string if computed flag is set", () => { const window = { getComputedStyle: () => {}, DOMException: globalThis.DOMException @@ -166,7 +166,28 @@ describe("CSSStyleDeclaration", () => { assert.strictEqual(style.getPropertyPriority("--foo"), "important"); }); - it("removeProperty throws if readonly flag is set", () => { + it("setProperty throws if read-only flag is set", () => { + const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException + }; + const style = new CSSStyleDeclaration(window); + style.setProperty("color", "green"); + style.setReadOnlyFlag(); + assert.throws( + () => { + style.setProperty("color", "red"); + }, + (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("removeProperty throws if read-only flag is set", () => { const window = { getComputedStyle: () => {}, DOMException: globalThis.DOMException @@ -175,9 +196,7 @@ describe("CSSStyleDeclaration", () => { style.setProperty("--foo", "green"); style.setProperty("--bar", "red"); assert.strictEqual(style.removeProperty("--foo"), "green"); - style.setOptions({ - readOnly: true - }); + style.setReadOnlyFlag(); assert.throws( () => { style.removeProperty("--bar"); From d4942ec283e9d7886f40bbe61c8473f2b563a410 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 13 Oct 2025 08:38:26 +0900 Subject: [PATCH 11/18] Update constructor --- lib/CSSStyleDeclaration.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index f9a1bd8e..85b636bc 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -30,12 +30,12 @@ class CSSStyleDeclaration { * @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 - * @param {boolean} opt.computed - The computed flag * @param {boolean} opt.readOnly - The read-only flag */ constructor(globalObject = globalThis, opt = {}) { - const { context } = opt; + const { context, format, onChange, readOnly, ...styleOpts } = opt; this._global = globalObject; this._ownerNode = null; this._parentRule = null; @@ -46,20 +46,24 @@ class CSSStyleDeclaration { this._parentRule = context; } } - this._onChange = opt.onChange; - this._readonly = Boolean(opt.readOnly); + this._onChange = onChange; + this._readonly = readOnly ?? false; this._computed = undefined; - if (opt.format === "computedValue") { + const styleOptions = { + ...styleOpts + }; + if (format === "computedValue") { this._computed = true; - } else { + styleOptions.format = format; + } else if (format !== "specifiedValue") { // Set the default format value. - opt.format = "specifiedValue"; + styleOptions.format = "specifiedValue"; } - this._options = opt; + this._options = styleOptions; this._values = new Map(); this._priorities = new Map(); this._length = 0; - this._updating = false; + this._updating = undefined; } get cssText() { @@ -173,7 +177,7 @@ class CSSStyleDeclaration { } catch { return; } - this._updating = false; + this._updating = undefined; if (typeof this._onChange === "function") { this._onChange(this.cssText); } From 9e2adadb68697fbacb01e4d7a3eea9ccba138de2 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 13 Oct 2025 11:03:28 +0900 Subject: [PATCH 12/18] Revert "Rename method" and fix method --- lib/CSSStyleDeclaration.js | 13 ++++++++++--- test/CSSStyleDeclaration.test.js | 14 +++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 85b636bc..eac11fa5 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -295,11 +295,18 @@ class CSSStyleDeclaration { /** * Non-standard. - * Use this method to set the read-only flag after getComputedStyle() has been resolved. + * To resolve getComputedStyle(), we need to setup additional option values. + * @param {object} opt - Options * @returns {void} */ - setReadOnlyFlag() { - this._readonly = true; + setOptions(opt = {}) { + for (const [key, value] of Object.entries(opt)) { + if (key === "readOnly") { + this._readonly = value; + } else { + this._options[key] = value; + } + } } // Internal methods diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 4b5d42ca..5ed09180 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -172,16 +172,18 @@ describe("CSSStyleDeclaration", () => { DOMException: globalThis.DOMException }; const style = new CSSStyleDeclaration(window); - style.setProperty("color", "green"); - style.setReadOnlyFlag(); + style.setProperty("--foo", "green"); + style.setOptions({ + readOnly: true + }); assert.throws( () => { - style.setProperty("color", "red"); + style.setProperty("--foo", "red"); }, (e) => { assert.strictEqual(e instanceof window.DOMException, true); assert.strictEqual(e.name, "NoModificationAllowedError"); - assert.strictEqual(e.message, "Property color can not be modified."); + assert.strictEqual(e.message, "Property --foo can not be modified."); return true; } ); @@ -196,7 +198,9 @@ describe("CSSStyleDeclaration", () => { style.setProperty("--foo", "green"); style.setProperty("--bar", "red"); assert.strictEqual(style.removeProperty("--foo"), "green"); - style.setReadOnlyFlag(); + style.setOptions({ + readOnly: true + }); assert.throws( () => { style.removeProperty("--bar"); From 1da158a123a611f4e4680b587f2919206bb2fe5e Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 13 Oct 2025 14:11:36 +0900 Subject: [PATCH 13/18] Update normalize.js --- lib/normalize.js | 61 ++++++++++++++++++++------------- test/CSSStyleProperties.test.js | 9 +++++ 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/lib/normalize.js b/lib/normalize.js index 341a9049..2126ab7f 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -16,8 +16,16 @@ const font = require("./properties/font"); const margin = require("./properties/margin"); const padding = require("./properties/padding"); +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"; @@ -28,7 +36,7 @@ const COLOR = "color"; const NONE = "none"; const shorthandProperties = new Map([ - ["background", background], + [BACKGROUND, background], [ BORDER, { @@ -41,13 +49,13 @@ const shorthandProperties = new Map([ ]) } ], - ["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], @@ -970,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); } @@ -1008,7 +1016,7 @@ 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(BORDER_IMAGE)) { @@ -1021,7 +1029,7 @@ 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(BORDER_IMAGE)) { @@ -1035,7 +1043,7 @@ 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(BORDER_IMAGE)) { @@ -1048,7 +1056,7 @@ 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(BORDER_IMAGE)) { @@ -1062,7 +1070,7 @@ 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(BORDER_IMAGE)) { @@ -1075,7 +1083,7 @@ 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(BORDER_IMAGE)) { @@ -1088,8 +1096,7 @@ 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(BORDER_IMAGE)) { @@ -1102,7 +1109,7 @@ 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(BORDER_IMAGE)) { @@ -1248,7 +1255,13 @@ const prepareProperties = (properties, opt = {}) => { } } if (!omitShorthandProperty) { - parsedProperties.set(property, { property, value, priority }); + if (property === BACKGROUND) { + if (priority) { + parsedProperties.set(property, { property, value, priority }); + } + } else { + parsedProperties.set(property, { property, value, priority }); + } } } else { parsedProperties.set(property, { property, value, priority }); diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js index c5600283..fb801514 100644 --- a/test/CSSStyleProperties.test.js +++ b/test/CSSStyleProperties.test.js @@ -1525,3 +1525,12 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () 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(); + 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"); + }); +}); From 4b86654ac243eb0832797fb0fc4db3f488130a81 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 13 Oct 2025 15:10:18 +0900 Subject: [PATCH 14/18] Fix background shorthand --- lib/properties/background.js | 20 ++++---------------- test/CSSStyleProperties.test.js | 3 +++ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/lib/properties/background.js b/lib/properties/background.js index 06015ba4..bf4c26f0 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -107,17 +107,11 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "background-position": { - const parsedValue = value.parse(part, { globalObject, options }); - if (parsedValue) { - bgPosition.push(parsedValue); - } + bgPosition.push(part); break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject, options }); - if (parsedValue) { - bgRepeat.push(parsedValue); - } + bgRepeat.push(part); break; } case "background-size": { @@ -166,17 +160,11 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject, options }); - if (parsedValue) { - bgRepeat.push(parsedValue); - } + bgRepeat.push(part); break; } case "background-size": { - const parsedValue = value.parse(part, { globalObject, options }); - if (parsedValue) { - bgSize.push(parsedValue); - } + bgSize.push(part); break; } default: { diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js index fb801514..9e81875d 100644 --- a/test/CSSStyleProperties.test.js +++ b/test/CSSStyleProperties.test.js @@ -1529,6 +1529,9 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () describe("regression test for https://github.com/jsdom/jsdom/issues/3944", () => { it("should get overwritten value", () => { const style = new CSSStyleProperties(); + 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"); From 92ac4dba8f0cdd5961a6f3b58740e9d7e93ff145 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 18 Oct 2025 22:13:10 +0900 Subject: [PATCH 15/18] Simplify cache --- lib/parsers.js | 9 +-- lib/utils/cache.js | 112 ++----------------------------- test/CSSStyleDeclaration.test.js | 52 ++++++-------- 3 files changed, 29 insertions(+), 144 deletions(-) diff --git a/lib/parsers.js b/lib/parsers.js index f4d8630c..3a062bc9 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -6,7 +6,7 @@ const { } = require("@asamuzakjp/css-color"); const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree"); const csstree = require("css-tree"); -const { createCacheKey, getCache, setCache } = require("./utils/cache"); +const { getCache, setCache } = require("./utils/cache"); const { asciiLowercase } = require("./utils/strings"); // CSS global keywords @@ -255,12 +255,7 @@ const parsePropertyValue = (prop, val, opt = {}) => { try { let cacheKey = ""; if (inArray) { - cacheKey = createCacheKey({ - name: "parsePropertyValue", - property: prop, - value: val, - caseSensitive - }); + cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`; const cachedValues = getCache(cacheKey); if (Array.isArray(cachedValues)) { return cachedValues; diff --git a/lib/utils/cache.js b/lib/utils/cache.js index f9a46f52..2669eb24 100644 --- a/lib/utils/cache.js +++ b/lib/utils/cache.js @@ -1,60 +1,22 @@ -// Forked from https://github.com/asamuzaK/cssColor/blob/main/src/js/cache.ts -// cssstyle calculates values using associated options. -// Even for the same property, the calculated result will differ depending on the option values. -// For example, if a keyData is `{ border-color: currentColor }` and the option for that keyData -// is `{ currentColor: red }`, then if the keyData is the same `{ border-color: currentColor }` -// but the option is `{ currentColor: green }`, the calculated result will be different. -// For this reason, we combine keyData and options to increase the uniqueness of cache keywords. - "use strict"; const { LRUCache } = require("lru-cache"); -class CacheItem { - #isNull; - #item; - - constructor(item, isNull = false) { - this.#item = item; - this.#isNull = Boolean(isNull); - } - - get item() { - return this.#item; - } - - get isNull() { - return this.#isNull; - } -} - -// The lru-cache requires values to be non-null, so we provide a NullObject for null values. -class NullObject extends CacheItem { - constructor() { - super(Symbol("null"), true); - } -} - -// lru-cache instance. +// 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) => { - if (key) { - if (value === null) { - lruCache.set(key, new NullObject()); - } else if (value instanceof CacheItem) { - lruCache.set(key, value); - } else { - lruCache.set(key, new CacheItem(value)); - } - } + lruCache.set(key, value === null ? nullSentinel : value); }; /** @@ -62,71 +24,11 @@ const setCache = (key, value) => { * @returns Cached item or false otherwise. */ const getCache = (key) => { - if (key && lruCache.has(key)) { - const cachedItem = lruCache.get(key); - if (cachedItem instanceof CacheItem) { - if (cachedItem.isNull) { - return null; - } - return cachedItem.item; - } - // Delete unexpected cached item. - lruCache.delete(key); - return false; - } - return false; -}; - -/** - * @param value - CSS value. - * @returns Stringified value in JSON notation. - */ -const valueToJsonString = (value) => { - if (typeof value === "undefined") { - return ""; - } - const res = JSON.stringify(value, (_key, val) => { - let replacedValue; - if (typeof val === "undefined") { - replacedValue = null; - } else if (typeof val === "function") { - replacedValue = val.name; - } else if (val instanceof Map || val instanceof Set) { - replacedValue = [...val]; - } else if (typeof val === "bigint") { - replacedValue = val.toString(); - } else { - replacedValue = val; - } - return replacedValue; - }); - return res; -}; - -/** - * Create stringified cacheKey from keyData and options. - * Note that if the options include a function, it will not be cached. - * @param keyData - Key data object. - * @param [opt] - Options. - * @returns Cache key. - */ -const createCacheKey = (keyData, opt = {}) => { - const { customProperty = {}, dimension = {} } = opt; - let cacheKey = ""; - if ( - keyData && - Object.keys(keyData).length && - typeof customProperty.callback !== "function" && - typeof dimension.callback !== "function" - ) { - keyData.opt = valueToJsonString(opt); - cacheKey = valueToJsonString(keyData); - } - return cacheKey; + const value = lruCache.get(key); + return value === nullSentinel ? null : value; }; module.exports = { - createCacheKey, getCache, setCache }; diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 5ed09180..b26654fa 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -5,8 +5,14 @@ const assert = require("node:assert/strict"); const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); describe("CSSStyleDeclaration", () => { + const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError + }; + it("has methods", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); assert.strictEqual(typeof style.item, "function"); assert.strictEqual(typeof style.getPropertyValue, "function"); assert.strictEqual(typeof style.setProperty, "function"); @@ -15,7 +21,7 @@ describe("CSSStyleDeclaration", () => { }); it("has attributes", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); assert.ok(style.__lookupGetter__("cssText")); assert.ok(style.__lookupSetter__("cssText")); assert.ok(style.__lookupGetter__("length")); @@ -24,9 +30,6 @@ describe("CSSStyleDeclaration", () => { }); it("sets internals for Element", () => { - const window = { - DOMException: globalThis.DOMException - }; const node = { nodeType: 1, style: {}, @@ -47,9 +50,6 @@ describe("CSSStyleDeclaration", () => { }); it("sets internals for CSSRule", () => { - const window = { - DOMException: globalThis.DOMException - }; const rule = { parentRule: {}, parentStyleSheet: { @@ -67,7 +67,7 @@ describe("CSSStyleDeclaration", () => { }); it("has format in internal options", () => { - const style = new CSSStyleDeclaration(null, { + const style = new CSSStyleDeclaration(window, { foo: "bar" }); assert.deepEqual(style._options, { @@ -77,7 +77,7 @@ describe("CSSStyleDeclaration", () => { }); it("should not override format if exists", () => { - const style = new CSSStyleDeclaration(null, { + const style = new CSSStyleDeclaration(window, { format: "computedValue" }); assert.deepEqual(style._options, { @@ -86,10 +86,6 @@ describe("CSSStyleDeclaration", () => { }); it("getting cssText returns empty string if computed flag is set", () => { - const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException - }; const style = new CSSStyleDeclaration(window, { format: "computedValue" }); @@ -98,7 +94,7 @@ describe("CSSStyleDeclaration", () => { }); it("setting improper css to cssText should not throw", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.cssText = "color: "; assert.strictEqual(style.cssText, ""); style.cssText = "color: red!"; @@ -106,13 +102,13 @@ describe("CSSStyleDeclaration", () => { }); it("item() throws if argument is not given", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); assert.throws( () => { style.item(); }, (e) => { - assert.strictEqual(e instanceof globalThis.TypeError, true); + assert.strictEqual(e instanceof window.TypeError, true); assert.strictEqual(e.message, "1 argument required, but only 0 present."); return true; } @@ -120,13 +116,13 @@ describe("CSSStyleDeclaration", () => { }); it("camelcase properties are not assigned with setproperty()", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.setProperty("fontSize", "12px"); assert.strictEqual(style.cssText, ""); }); it("custom properties are case-sensitive", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.cssText = "--fOo: purple"; assert.strictEqual(style.getPropertyValue("--foo"), ""); @@ -134,43 +130,39 @@ describe("CSSStyleDeclaration", () => { }); 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("getPropertyPriority for property", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.setProperty("color", "green", "important"); assert.strictEqual(style.getPropertyPriority("color"), "important"); }); it("getPropertyPriority for custom property", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.setProperty("--foo", "green", "important"); assert.strictEqual(style.getPropertyPriority("--foo"), "important"); }); it("setProperty throws if read-only flag is set", () => { - const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException - }; const style = new CSSStyleDeclaration(window); style.setProperty("--foo", "green"); style.setOptions({ @@ -190,10 +182,6 @@ describe("CSSStyleDeclaration", () => { }); it("removeProperty throws if read-only flag is set", () => { - const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException - }; const style = new CSSStyleDeclaration(window); style.setProperty("--foo", "green"); style.setProperty("--bar", "red"); From d6dfd3f249acec671e1da5629eaed8f73e921548 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 18 Oct 2025 22:45:26 +0900 Subject: [PATCH 16/18] Update normalize.js --- lib/normalize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/normalize.js b/lib/normalize.js index 2126ab7f..2b6110ee 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -1256,7 +1256,7 @@ const prepareProperties = (properties, opt = {}) => { } if (!omitShorthandProperty) { if (property === BACKGROUND) { - if (priority) { + if (hasVarFunc(value) || priority) { parsedProperties.set(property, { property, value, priority }); } } else { From 711a11adafb388e5aa038b1366e6a05e868c1114 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 18 Oct 2025 22:45:59 +0900 Subject: [PATCH 17/18] Update constructor --- lib/CSSStyleDeclaration.js | 60 ++++--- test/CSSStyleDeclaration.test.js | 1 - test/CSSStyleProperties.test.js | 266 +++++++++++++++---------------- test/properties.test.js | 8 +- 4 files changed, 174 insertions(+), 161 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index eac11fa5..42beceee 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -22,6 +22,8 @@ const { } = require("./parsers"); const { asciiLowercase } = require("./utils/strings"); +const ELEMENT_NODE = 1; + /** * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface */ @@ -32,38 +34,39 @@ class CSSStyleDeclaration { * @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 - * @param {boolean} opt.readOnly - The read-only flag */ - constructor(globalObject = globalThis, opt = {}) { - const { context, format, onChange, readOnly, ...styleOpts } = 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 (context.nodeType === 1) { + // 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; } } - this._onChange = onChange; - this._readonly = readOnly ?? false; - this._computed = undefined; - const styleOptions = { - ...styleOpts - }; - if (format === "computedValue") { - this._computed = true; - styleOptions.format = format; - } else if (format !== "specifiedValue") { - // Set the default format value. - styleOptions.format = "specifiedValue"; - } - this._options = styleOptions; + 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._updating = undefined; + + // 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() { @@ -301,10 +304,21 @@ class CSSStyleDeclaration { */ setOptions(opt = {}) { for (const [key, value] of Object.entries(opt)) { - if (key === "readOnly") { - this._readonly = value; - } else { - this._options[key] = value; + switch (key) { + case "format": { + if (value === "computedValue") { + this._computed = true; + this._options[key] = value; + } + break; + } + case "readOnly": { + this._readonly = value === true; + break; + } + default: { + this._options[key] = value; + } } } } diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index b26654fa..d0c82680 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -71,7 +71,6 @@ describe("CSSStyleDeclaration", () => { foo: "bar" }); assert.deepEqual(style._options, { - foo: "bar", format: "specifiedValue" }); }); diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js index 9e81875d..8f92dede 100644 --- a/test/CSSStyleProperties.test.js +++ b/test/CSSStyleProperties.test.js @@ -7,14 +7,20 @@ 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(); + const style = new CSSStyleProperties(window); assert.strictEqual(style instanceof CSSStyleDeclaration, true); }); it("all dashed properties are included in propertyList", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); for (const i in style) { if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { assert.strictEqual(propertyList.has(i), true, i); @@ -23,7 +29,7 @@ describe("CSSStyleProperties", () => { }); it("has camelCased property for dashed property", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); for (const i in style) { if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { const camel = camelize.dashedToCamelCase(i); @@ -34,7 +40,7 @@ describe("CSSStyleProperties", () => { // FIXME: https://github.com/jsdom/cssstyle/issues/210 it.skip("all webkit prefixed properties are included in propertyList", () => { - const style = new CSSStyleProperties(); + 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); @@ -43,7 +49,7 @@ describe("CSSStyleProperties", () => { }); it("has camelCased property for webkit prefixed property", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); for (const i in style) { if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { const camel = camelize.dashedToCamelCase(i); @@ -53,7 +59,7 @@ describe("CSSStyleProperties", () => { }); it("has PascalCased property for webkit prefixed property", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); for (const i in style) { if (/^webkit[A-Z]/.test(i)) { const pascal = i.replace(/^webkit/, "Webkit"); @@ -63,21 +69,21 @@ describe("CSSStyleProperties", () => { }); it("setting cssFloat should also set float", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; assert.strictEqual(style.length, 4); assert.strictEqual( @@ -94,7 +100,7 @@ describe("CSSStyleProperties", () => { }); it("from properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.color = "blue"; assert.strictEqual(style.length, 1); assert.strictEqual(style[0], "color"); @@ -112,7 +118,7 @@ describe("CSSStyleProperties", () => { }); it("shorthand properties", () => { - const style = new CSSStyleProperties(); + 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")'); @@ -130,7 +136,7 @@ describe("CSSStyleProperties", () => { }); it("width and height properties and null and empty strings", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.height = 6; assert.strictEqual(style.height, ""); style.width = 0; @@ -150,7 +156,7 @@ describe("CSSStyleProperties", () => { }); it("implicit properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderWidth = 0; assert.strictEqual(style.border, ""); assert.strictEqual(style.borderWidth, "0px"); @@ -162,7 +168,7 @@ describe("CSSStyleProperties", () => { }); it("top, left, right, bottom properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.top = 0; style.left = "0%"; style.right = "5em"; @@ -176,7 +182,7 @@ describe("CSSStyleProperties", () => { }); it('top, left, right, bottom properties should accept "auto"', () => { - const style = new CSSStyleProperties(); + 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"); @@ -185,7 +191,7 @@ describe("CSSStyleProperties", () => { }); it("clear and clip properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.clear = "none"; assert.strictEqual(style.clear, "none"); style.clear = "lfet"; @@ -206,7 +212,7 @@ describe("CSSStyleProperties", () => { }); it("colors", () => { - const style = new CSSStyleProperties(); + 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)"; @@ -234,24 +240,24 @@ describe("CSSStyleProperties", () => { }); it("invalid hex color value", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.color = "#1234567"; assert.strictEqual(style.color, ""); }); it("shorthand properties with embedded spaces", () => { - let style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.borderWidth = "1px"; assert.strictEqual(style.cssText, "border-width: 1px;"); style.border = ""; @@ -259,7 +265,7 @@ describe("CSSStyleProperties", () => { }); it("setting implicit properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderTopWidth = "1px"; assert.strictEqual(style.cssText, "border-top-width: 1px;"); style.borderWidth = ""; @@ -267,7 +273,7 @@ describe("CSSStyleProperties", () => { }); it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderTopWidth = "2px"; assert.strictEqual(style.cssText, "border-top-width: 2px;"); style.border = ""; @@ -279,7 +285,7 @@ describe("CSSStyleProperties", () => { }); it("set border as none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "none"; assert.strictEqual(style.border, "medium", "border"); assert.strictEqual(style.borderWidth, "medium", "border-width"); @@ -292,7 +298,7 @@ describe("CSSStyleProperties", () => { }); it("set border as none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "none"; assert.strictEqual(style.border, "medium", "border"); assert.strictEqual(style.borderWidth, "medium", "border-width"); @@ -305,7 +311,7 @@ describe("CSSStyleProperties", () => { }); it("set border-style as none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderStyle = "none"; assert.strictEqual(style.border, "", "border"); assert.strictEqual(style.borderWidth, "", "border-width"); @@ -318,7 +324,7 @@ describe("CSSStyleProperties", () => { }); it("set border-top as none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderTop = "none"; assert.strictEqual(style.border, "", "border"); assert.strictEqual(style.borderWidth, "", "border-width"); @@ -331,7 +337,7 @@ describe("CSSStyleProperties", () => { }); it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "1px"; style.borderStyle = "none"; assert.strictEqual(style.border, "1px", "border"); @@ -345,7 +351,7 @@ describe("CSSStyleProperties", () => { }); it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "1px"; style.borderStyle = "none"; assert.strictEqual(style.border, "1px", "border"); @@ -359,7 +365,7 @@ describe("CSSStyleProperties", () => { }); it("set border as 1px and change border-top to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "1px"; style.borderTop = "none"; assert.strictEqual(style.border, "", "border"); @@ -377,7 +383,7 @@ describe("CSSStyleProperties", () => { }); it("set border as 1px solid and change border-top to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "1px solid"; style.borderTop = "none"; assert.strictEqual(style.border, "", "border"); @@ -395,7 +401,7 @@ describe("CSSStyleProperties", () => { }); it("set border as none and change border-style to null", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "none"; style.borderStyle = null; assert.strictEqual(style.border, "", "border"); @@ -413,7 +419,7 @@ describe("CSSStyleProperties", () => { }); it("set border as solid and change border-top to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "solid"; style.borderTop = "none"; assert.strictEqual(style.border, "", "border"); @@ -431,7 +437,7 @@ describe("CSSStyleProperties", () => { }); it("set border as solid and change border-style to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "solid"; style.borderStyle = "none"; assert.strictEqual(style.border, "medium", "border"); @@ -445,7 +451,7 @@ describe("CSSStyleProperties", () => { }); it("set border-style as solid and change border-top to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderStyle = "solid"; style.borderTop = "none"; assert.strictEqual(style.border, "", "border"); @@ -463,7 +469,7 @@ describe("CSSStyleProperties", () => { }); it("set border-top as solid and change border-style to none", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderTop = "solid"; style.borderStyle = "none"; assert.strictEqual(style.border, "", "border"); @@ -481,7 +487,7 @@ describe("CSSStyleProperties", () => { }); it("set border-style as solid and change border-top to null", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderStyle = "solid"; style.borderTop = null; assert.strictEqual( @@ -499,7 +505,7 @@ describe("CSSStyleProperties", () => { }); it("setting border values to none should change dependent values", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderTopWidth = "1px"; assert.strictEqual(style.cssText, "border-top-width: 1px;"); style.border = "none"; @@ -551,21 +557,21 @@ describe("CSSStyleProperties", () => { }); it("setting border to green", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.border = "initial"; assert.strictEqual(style.cssText, "border: initial;"); assert.strictEqual(style.border, "initial"); @@ -580,7 +586,7 @@ describe("CSSStyleProperties", () => { }); it("setting borderTop to initial should set top related properties initial", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderTop = "initial"; assert.strictEqual(style.cssText, "border-top: initial;"); assert.strictEqual(style.border, ""); @@ -595,26 +601,26 @@ describe("CSSStyleProperties", () => { }); it("setting border to 0 should be okay", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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"); @@ -625,7 +631,7 @@ describe("CSSStyleProperties", () => { }); it("setting opacity should work", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.setProperty("opacity", 0.75); assert.strictEqual(style.cssText, "opacity: 0.75;"); style.opacity = "0.50"; @@ -635,18 +641,18 @@ describe("CSSStyleProperties", () => { }); it("width and height of auto should work", () => { - let style = new CSSStyleProperties(); + let style = new CSSStyleProperties(window); style.width = "auto"; assert.strictEqual(style.cssText, "width: auto;"); assert.strictEqual(style.width, "auto"); - style = new CSSStyleProperties(); + 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(); + 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"); @@ -666,7 +672,7 @@ describe("CSSStyleProperties", () => { }); it("padding and margin should set/clear shorthand properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const parts = ["Top", "Right", "Bottom", "Left"]; const testParts = function (name, v, V) { style[name] = v; @@ -693,7 +699,7 @@ describe("CSSStyleProperties", () => { }); it("padding and margin shorthands should set main properties", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const parts = ["Top", "Right", "Bottom", "Left"]; const testParts = function (name, v, V) { let expected; @@ -713,7 +719,7 @@ describe("CSSStyleProperties", () => { }); it("setting individual padding and margin properties to an empty string should clear them", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const properties = ["padding", "margin"]; const parts = ["Top", "Right", "Bottom", "Left"]; @@ -730,7 +736,7 @@ describe("CSSStyleProperties", () => { }); it("removing and setting individual margin properties updates the combined property accordingly", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.margin = "1px 2px 3px 4px"; style.marginTop = ""; assert.strictEqual(style.margin, ""); @@ -755,7 +761,7 @@ describe("CSSStyleProperties", () => { 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(); + const style = new CSSStyleProperties(window); const parts = ["Top", "Right", "Bottom", "Left"]; const partValues = ["1px", "2px", "3px", "4px"]; @@ -782,7 +788,7 @@ describe("CSSStyleProperties", () => { }); it(`setting additional ${property} properties keeps important status of others`, () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const importantProperty = `${property}-top: 3px !important;`; style.cssText = importantProperty; assert.strictEqual(style.cssText.includes(importantProperty), true); @@ -798,7 +804,7 @@ describe("CSSStyleProperties", () => { }); it(`setting individual ${property} keeps important status of others`, () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = `${property}: 3px !important;`; style[`${property}Top`] = "4px"; assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); @@ -810,15 +816,12 @@ describe("CSSStyleProperties", () => { } it("setting a value to 0 should return the string value", () => { - const style = new CSSStyleProperties(); + 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 window = { - DOMException: globalThis.DOMException - }; const node = { nodeType: 1, style: {}, @@ -841,9 +844,6 @@ describe("CSSStyleProperties", () => { }); it("onchange callback should be called only once when multiple properties were added", () => { - const window = { - DOMException: globalThis.DOMException - }; const node = { nodeType: 1, style: {}, @@ -864,9 +864,6 @@ describe("CSSStyleProperties", () => { }); it("onchange callback should not be called when property is set to the same value", () => { - const window = { - DOMException: globalThis.DOMException - }; const node = { nodeType: 1, style: {}, @@ -889,9 +886,6 @@ describe("CSSStyleProperties", () => { }); it("onchange callback should not be called when removeProperty was called on non-existing property", () => { - const window = { - DOMException: globalThis.DOMException - }; const node = { nodeType: 1, style: {}, @@ -911,7 +905,7 @@ describe("CSSStyleProperties", () => { }); it("setting improper css to csstext should not throw", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = "color: "; assert.strictEqual(style.cssText, ""); style.color = "black"; @@ -920,7 +914,7 @@ describe("CSSStyleProperties", () => { }); it("url parsing works with quotes", () => { - const style = new CSSStyleProperties(); + 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')"; @@ -930,7 +924,7 @@ describe("CSSStyleProperties", () => { }); it("setting 0 to a padding or margin works", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.padding = 0; assert.strictEqual(style.cssText, "padding: 0px;"); style.margin = "1em"; @@ -939,7 +933,7 @@ describe("CSSStyleProperties", () => { }); it("setting ex units to a padding or margin works", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.padding = "1ex"; assert.strictEqual(style.cssText, "padding: 1ex;"); style.margin = "1em"; @@ -948,7 +942,7 @@ describe("CSSStyleProperties", () => { }); it("setting empty string and null to a padding or margin works", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const parts = ["Top", "Right", "Bottom", "Left"]; function testParts(base, nullValue) { const props = [base].concat(parts.map((part) => base + part)); @@ -968,7 +962,7 @@ describe("CSSStyleProperties", () => { }); it("setting undefined to a padding or margin does nothing", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const parts = ["Top", "Right", "Bottom", "Left"]; function testParts(base) { const props = [base].concat(parts.map((part) => base + part)); @@ -985,7 +979,7 @@ describe("CSSStyleProperties", () => { }); it("setting null to background works", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.background = "red"; assert.strictEqual(style.cssText, "background: red;"); style.background = null; @@ -993,7 +987,7 @@ describe("CSSStyleProperties", () => { }); it("flex properties should keep their values", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.flexDirection = "column"; assert.strictEqual(style.cssText, "flex-direction: column;"); style.flexDirection = "row"; @@ -1001,33 +995,33 @@ describe("CSSStyleProperties", () => { }); it("camelcase properties are not assigned with `.setproperty()`", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.setProperty("fontSize", "12px"); assert.strictEqual(style.cssText, ""); }); it("casing is ignored in `.setproperty()`", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.setProperty("flex-shrink", 0); assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); style.setProperty("flex-shrink", 1); @@ -1036,14 +1030,14 @@ describe("CSSStyleProperties", () => { }); it("flex-grow works", () => { - const style = new CSSStyleProperties(); + 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(); + const style = new CSSStyleProperties(window); style.setProperty("flex-basis", 0); assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); style.setProperty("flex-basis", "250px"); @@ -1056,7 +1050,7 @@ describe("CSSStyleProperties", () => { }); it("shorthand flex works", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.setProperty("flex", "none"); assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); @@ -1092,7 +1086,7 @@ describe("CSSStyleProperties", () => { }); it("font-size get a valid value", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const invalidValue = "1r5px"; style.cssText = "font-size: 15px"; assert.strictEqual(1, style.length); @@ -1102,28 +1096,28 @@ describe("CSSStyleProperties", () => { }); it("getPropertyValue for custom properties in cssText", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style["--baz"] = "yellow"; assert.strictEqual(style.getPropertyValue("--baz"), ""); }); it("custom properties are case-sensitive", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = "--fOo: purple"; assert.strictEqual(style.getPropertyValue("--foo"), ""); @@ -1140,62 +1134,62 @@ describe("CSSStyleProperties", () => { "padding" ]) { it(`supports calc for ${property}`, () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); assert.strictEqual( style.getPropertyValue("line-height"), @@ -1204,31 +1198,31 @@ describe("CSSStyleProperties", () => { }); it("supports abs", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.setProperty("width", "10px"); assert.strictEqual(style.getPropertyValue("width"), "10px"); @@ -1240,26 +1234,26 @@ describe("CSSStyleProperties", () => { }); it("shorthand serialization with shorthand and longhands mixed", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.cssText = "border-width: 1px;"; assert.strictEqual(style.cssText, "border-width: 1px;"); }); it("shorthand serialization", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = "border: 1px; border-top: 1px !important;"; assert.strictEqual( style.cssText, @@ -1268,19 +1262,19 @@ describe("CSSStyleProperties", () => { }); it("set cssText as none", () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.cssText = "flex: initial;"; assert.strictEqual(style.flex, "initial"); assert.strictEqual(style.flexGrow, "initial"); @@ -1290,7 +1284,7 @@ describe("CSSStyleProperties", () => { }); it("single value flex with non-CSS-wide value", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = "flex: 0;"; assert.strictEqual(style.flex, "0 1 0%"); assert.strictEqual(style.flexGrow, "0"); @@ -1300,7 +1294,7 @@ describe("CSSStyleProperties", () => { }); it("multiple values flex with CSS-wide keyword", () => { - const style = new CSSStyleProperties(); + 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"); @@ -1310,7 +1304,7 @@ describe("CSSStyleProperties", () => { }); it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = "flex: initial; flex-shrink: 0;"; assert.strictEqual(style.flex, ""); assert.strictEqual(style.flexGrow, "initial"); @@ -1320,7 +1314,7 @@ describe("CSSStyleProperties", () => { }); it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; assert.strictEqual(style.flex, ""); assert.strictEqual(style.flexGrow, "initial"); @@ -1333,7 +1327,7 @@ describe("CSSStyleProperties", () => { /* regression tests */ describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { it("should set global value unset", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.setProperty("width", "10px"); assert.strictEqual(style.getPropertyValue("width"), "10px"); @@ -1344,7 +1338,7 @@ describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { it("should not set custom properties twice", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.setProperty("--foo", 0); style.setProperty("--foo", 1); @@ -1357,13 +1351,13 @@ describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { it("should set stringified value", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.setProperty("--foo", true); assert.strictEqual(style.getPropertyValue("--foo"), "true"); }); it("throws for setting Symbol", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); assert.throws( () => style.setProperty("width", Symbol("foo")), (e) => { @@ -1387,21 +1381,21 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { it('returns empty string for "webkit-*", without leading "-"', () => { - const style = new CSSStyleProperties(); + 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(); + 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(); + const style = new CSSStyleProperties(window); style.cssText = "background-color: green; -webkit-foo: scale(3);"; assert.strictEqual(style.backgroundColor, "green"); assert.strictEqual(style.webkitFoo, undefined); @@ -1410,7 +1404,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () = describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { it("no-op when setting undefined to border", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.border = "1px solid green"; assert.strictEqual(style.border, "1px solid green"); style.border = undefined; @@ -1418,7 +1412,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () }); it("no-op when setting undefined to borderWidth", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.borderWidth = "1px"; assert.strictEqual(style.borderWidth, "1px"); style.border = undefined; @@ -1441,7 +1435,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () "ui-monospace", "ui-rounded" ]; - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); for (const keyword of keywords) { style.fontFamily = keyword; assert.strictEqual(style.fontFamily, keyword); @@ -1455,7 +1449,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () "generic(khmer-mul)", "generic(nastaliq)" ]; - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); for (const keyword of keywords) { style.fontFamily = keyword; assert.strictEqual(style.fontFamily, keyword); @@ -1465,7 +1459,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () // 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(); + const style = new CSSStyleProperties(window); for (const keyword of keywords) { style.fontFamily = keyword; assert.strictEqual(style.fontFamily, keyword); @@ -1473,7 +1467,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () }); it("should support `-webkit-` prefixed family name", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.fontFamily = "-webkit-body"; assert.strictEqual(style.fontFamily, "-webkit-body"); }); @@ -1481,7 +1475,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { it("should get normalized value for font shorthand", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.font = "normal bold 4px sans-serif"; assert.strictEqual(style.font, "bold 4px sans-serif"); }); @@ -1489,7 +1483,7 @@ describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { it("should return value for each property", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); const key = "background-color"; const camel = "backgroundColor"; const value = "var(--foo)"; @@ -1501,7 +1495,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () }); it("should set var() values for background-attachment correctly", () => { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); style.backgroundAttachment = "var(--foo)"; assert.strictEqual(style.backgroundAttachment, "var(--foo)"); style.setProperty("background-attachment", "var(--bar)"); @@ -1509,7 +1503,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () }); it("should allow changing a single property on a border, when border contains a css variable", () => { - const style = new CSSStyleProperties(); + 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"; @@ -1518,7 +1512,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () }); it("should get value and priority", () => { - const style = new CSSStyleProperties(); + 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"); @@ -1528,7 +1522,7 @@ describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () describe("regression test for https://github.com/jsdom/jsdom/issues/3944", () => { it("should get overwritten value", () => { - const style = new CSSStyleProperties(); + 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"); diff --git a/test/properties.test.js b/test/properties.test.js index e4b87f83..1eedd9ce 100644 --- a/test/properties.test.js +++ b/test/properties.test.js @@ -4,8 +4,14 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); const { CSSStyleProperties } = require("../lib/CSSStyleProperties"); +const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError +}; + function testPropertyValue(property, value, expected) { - const style = new CSSStyleProperties(); + const style = new CSSStyleProperties(window); let res; style.setProperty(property, value); From c59ba6c0942650faa90ddb1606b35b85221cb06c Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 27 Oct 2025 22:34:32 +0900 Subject: [PATCH 18/18] Revert "Merge branch 'main' into style2" This reverts commit df32d4af5df5a58927f8be6699a0a97367c2522e, reversing changes made to 711a11adafb388e5aa038b1366e6a05e868c1114. --- lib/CSSStyleDeclaration.js | 12 ----- test/CSSStyleDeclaration.test.js | 88 ++------------------------------ 2 files changed, 3 insertions(+), 97 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 48495f51..42beceee 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -128,19 +128,7 @@ class CSSStyleDeclaration { ); 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, diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index d149166c..d0c82680 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -37,91 +37,9 @@ describe("CSSStyleDeclaration", () => { defaultView: window } }; - 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 - } - } - }; - 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(window, { context: node,