From 7d550ef32847c02d896c84fcaf3ad35a8c83fca5 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 1 Nov 2025 11:15:46 +0900 Subject: [PATCH 01/29] add --- .github/workflows/build.yml | 4 +- .gitignore | 2 +- lib/CSSStyleDeclaration.js | 728 ++--- lib/CSSStyleProperties.js | 38 + lib/generated/allProperties.js | 4 +- lib/generated/implementedProperties.js | 313 ++ lib/index.js | 11 + lib/normalize.js | 533 ++-- lib/parsers.js | 151 +- lib/properties/background.js | 98 +- 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 | 24 +- lib/properties/borderBottom.js | 24 +- 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 | 24 +- lib/properties/borderLeftColor.js | 8 +- lib/properties/borderLeftStyle.js | 6 +- lib/properties/borderLeftWidth.js | 14 +- lib/properties/borderRight.js | 24 +- 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 | 24 +- 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 | 17 +- 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 | 35 +- 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/cache.js | 34 + lib/utils/propertyDescriptors.js | 64 +- package-lock.json | 2634 +++-------------- package.json | 41 +- {lib/utils => scripts}/camelize.js | 2 +- scripts/downloadLatestProperties.mjs | 14 +- scripts/generateImplementedProperties.mjs | 42 - scripts/generateProperties.js | 2 +- scripts/generatePropertyList.mjs | 47 + test/CSSStyleDeclaration.test.js | 1639 +--------- ...ies.test.js => CSSStyleProperties.test.js} | 1671 ++++++++++- test/camelize.test.js | 2 +- test/parsers.test.js | 47 +- 99 files changed, 4075 insertions(+), 4861 deletions(-) create mode 100644 lib/CSSStyleProperties.js create mode 100644 lib/generated/implementedProperties.js create mode 100644 lib/index.js create mode 100644 lib/utils/cache.js rename {lib/utils => scripts}/camelize.js (94%) delete mode 100644 scripts/generateImplementedProperties.mjs create mode 100644 scripts/generatePropertyList.mjs rename test/{properties.test.js => CSSStyleProperties.test.js} (50%) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fa8b7eb7..e116e733 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,8 +18,8 @@ jobs: - lts/* - latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-node@v5 with: node-version: ${{ matrix.node-version }} - run: npm ci diff --git a/.gitignore b/.gitignore index 8297d482..758d4490 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules npm-debug.log -lib/generated/implementedProperties.js lib/generated/properties.js +lib/generated/propertyList.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index a451cf9e..79f461f8 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -4,13 +4,11 @@ */ "use strict"; -const allProperties = require("./generated/allProperties"); -const implementedProperties = require("./generated/implementedProperties"); -const generatedProperties = require("./generated/properties"); +const propertyList = require("./generated/propertyList"); const { borderProperties, getPositionValue, - normalizeBorderProperties, + normalizeProperties, prepareBorderProperties, prepareProperties, shorthandProperties @@ -22,113 +20,53 @@ const { parsePropertyValue, prepareValue } = require("./parsers"); -const allExtraProperties = require("./utils/allExtraProperties"); -const { dashedToCamelCase } = require("./utils/camelize"); -const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); +const ELEMENT_NODE = 1; + /** * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface */ class CSSStyleDeclaration { /** - * @param {Function} onChangeCallback - * @param {object} [opt] - * @param {object} [opt.context] - Window, Element or CSSRule. + * @param {object} globalObject - Window + * @param {object} opt - Options + * @param {object} opt.context - Element or CSSStyleRule + * @param {string} opt.format - "specifiedValue" or "computedValue" + * @param {Function} opt.onChange - Callback when cssText is changed or the property is removed */ - constructor(onChangeCallback, opt = {}) { - // Make constructor and internals non-enumerable. - Object.defineProperties(this, { - constructor: { - enumerable: false, - writable: true - }, - - // Window - _global: { - value: globalThis, - enumerable: false, - writable: true - }, - - // Element - _ownerNode: { - value: null, - enumerable: false, - writable: true - }, - - // CSSRule - _parentNode: { - value: null, - enumerable: false, - writable: true - }, - - _onChange: { - value: null, - enumerable: false, - writable: true - }, - - _values: { - value: new Map(), - enumerable: false, - writable: true - }, - - _priorities: { - value: new Map(), - enumerable: false, - writable: true - }, - - _length: { - value: 0, - enumerable: false, - writable: true - }, - - _computed: { - value: false, - enumerable: false, - writable: true - }, - - _readonly: { - value: false, - enumerable: false, - writable: true - }, - - _setInProgress: { - value: false, - enumerable: false, - writable: true - } - }); - - const { context } = opt; + constructor(globalObject, opt = {}) { + const { context, format, onChange } = opt; + // These help interface with jsdom. + this._global = globalObject; + this._onChange = onChange; + + // These correspond to https://drafts.csswg.org/cssom/#css-declaration-block. + this._computed = format === "computedValue"; + this._ownerNode = null; + this._parentRule = null; if (context) { - if (typeof context.getComputedStyle === "function") { - this._global = context; - this._computed = true; - this._readonly = true; - } else if (context.nodeType === 1 && Object.hasOwn(context, "style")) { - this._global = context.ownerDocument.defaultView; + // The context is an element. + if (context.nodeType === ELEMENT_NODE) { this._ownerNode = context; + // The context is a CSSStyleRule. } else if (Object.hasOwn(context, "parentRule")) { this._parentRule = context; - // Find Window from the owner node of the StyleSheet. - const window = context?.parentStyleSheet?.ownerNode?.ownerDocument?.defaultView; - if (window) { - this._global = window; - } } } - if (typeof onChangeCallback === "function") { - this._onChange = onChangeCallback; - } + this._readonly = false; + this._updating = false; + + // These correspond to the specification's "declarations". + this._values = new Map(); + this._priorities = new Map(); + this._length = 0; + + // This is used internally by parsers. e.g. parsers.resolveCalc(), parsers.parseColor(), etc. + // Note that options may be updated later to resolve getComputedStyle(). See setOptions() below. + this._options = { + format: format === "computedValue" ? format : "specifiedValue" + }; } get cssText() { @@ -150,7 +88,7 @@ class CSSStyleDeclaration { } properties.set(property, { property, value, priority }); } - const normalizedProperties = normalizeBorderProperties(properties); + const normalizedProperties = normalizeProperties(properties); const parts = []; for (const { property, value, priority } of normalizedProperties.values()) { if (priority) { @@ -168,19 +106,23 @@ class CSSStyleDeclaration { const name = "NoModificationAllowedError"; throw new this._global.DOMException(msg, name); } + if (this._updating) { + return; + } Array.prototype.splice.call(this, 0, this._length); this._values.clear(); this._priorities.clear(); - if (this._parentRule || (this._ownerNode && this._setInProgress)) { - return; - } - this._setInProgress = true; + this._updating = true; try { + // TBD: use cache? const valueObj = parseCSS( val, { - context: "declarationList", - parseValue: false + globalObject: this._global, + options: { + context: "declarationList", + parseValue: false + } }, true ); @@ -199,12 +141,12 @@ class CSSStyleDeclaration { shouldSkipNext = false; continue; } - const { - important, - property, - value: { value } - } = item; - if (typeof property === "string" && typeof value === "string") { + if (item.type === "Declaration") { + const { + important, + property, + value: { value } + } = item; const priority = important ? "important" : ""; const isCustomProperty = property.startsWith("--"); if (isCustomProperty || hasVarFunc(value)) { @@ -217,8 +159,10 @@ class CSSStyleDeclaration { properties.set(property, { property, value, priority }); } } else { + // TBD: use cache? const parsedValue = parsePropertyValue(property, value, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (parsedValue) { if (properties.has(property)) { @@ -236,7 +180,8 @@ class CSSStyleDeclaration { } } const parsedProperties = prepareProperties(properties, { - globalObject: this._global + globalObject: this._global, + options: this._options }); for (const [property, item] of parsedProperties) { const { priority, value } = item; @@ -247,7 +192,7 @@ class CSSStyleDeclaration { } catch { return; } - this._setInProgress = false; + this._updating = undefined; if (typeof this._onChange === "function") { this._onChange(this.cssText); } @@ -258,8 +203,7 @@ class CSSStyleDeclaration { } // This deletes indices if the new length is less then the current length. - // If the new length is more, it does nothing, the new indices will be - // undefined until set. + // If the new length is more, it does nothing, the new indices will be undefined until set. set length(len) { for (let i = len; i < this._length; i++) { delete this[i]; @@ -267,36 +211,6 @@ class CSSStyleDeclaration { this._length = len; } - // Readonly - get parentRule() { - return this._parentRule; - } - - get cssFloat() { - return this.getPropertyValue("float"); - } - - set cssFloat(value) { - this._setProperty("float", value); - } - - /** - * @param {string} property - */ - getPropertyPriority(property) { - return this._priorities.get(property) || ""; - } - - /** - * @param {string} property - */ - getPropertyValue(property) { - if (this._values.has(property)) { - return this._values.get(property).toString(); - } - return ""; - } - /** * @param {...number} args */ @@ -316,26 +230,18 @@ class CSSStyleDeclaration { /** * @param {string} property */ - removeProperty(property) { - if (this._readonly) { - const msg = `Property ${property} can not be modified.`; - const name = "NoModificationAllowedError"; - throw new this._global.DOMException(msg, name); - } - if (!this._values.has(property)) { - return ""; - } - const prevValue = this._values.get(property); - this._values.delete(property); - this._priorities.delete(property); - const index = Array.prototype.indexOf.call(this, property); - if (index >= 0) { - Array.prototype.splice.call(this, index, 1); - if (typeof this._onChange === "function") { - this._onChange(this.cssText); - } + getPropertyValue(property) { + if (this._values.has(property)) { + return this._values.get(property).toString(); } - return prevValue; + return ""; + } + + /** + * @param {string} property + */ + getPropertyPriority(property) { + return this._priorities.get(property) || ""; } /** @@ -362,7 +268,7 @@ class CSSStyleDeclaration { return; } const property = asciiLowercase(prop); - if (!allProperties.has(property) && !allExtraProperties.has(property)) { + if (!propertyList.has(property)) { return; } if (priority) { @@ -372,277 +278,297 @@ class CSSStyleDeclaration { } this[property] = value; } -} -// Internal methods -Object.defineProperties(CSSStyleDeclaration.prototype, { - _setProperty: { - /** - * @param {string} property - * @param {string} val - * @param {string} priority - */ - value(property, val, priority) { - if (typeof val !== "string") { - return; - } - if (val === "") { - this.removeProperty(property); - return; - } - let originalText = ""; + /** + * @param {string} property + */ + removeProperty(property) { + if (this._readonly) { + const msg = `Property ${property} can not be modified.`; + const name = "NoModificationAllowedError"; + throw new this._global.DOMException(msg, name); + } + if (!this._values.has(property)) { + return ""; + } + const prevValue = this._values.get(property); + this._values.delete(property); + this._priorities.delete(property); + const index = Array.prototype.indexOf.call(this, property); + if (index >= 0) { + Array.prototype.splice.call(this, index, 1); if (typeof this._onChange === "function") { - originalText = this.cssText; - } - if (this._values.has(property)) { - const index = Array.prototype.indexOf.call(this, property); - // The property already exists but is not indexed into `this` so add it. - if (index < 0) { - this[this._length] = property; - this._length++; - } - } else { - // New property. - this[this._length] = property; - this._length++; - } - if (priority === "important") { - this._priorities.set(property, priority); - } else { - this._priorities.delete(property); - } - this._values.set(property, val); - if ( - typeof this._onChange === "function" && - this.cssText !== originalText && - !this._setInProgress - ) { this._onChange(this.cssText); } - }, - enumerable: false - }, + } + return prevValue; + } - _borderSetter: { - /** - * @param {string} prop - * @param {object|Array|string} val - * @param {string} prior - */ - value(prop, val, prior) { - const properties = new Map(); - if (prop === "border") { - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - properties.set(prop, { propery: prop, value: val, priority }); - } else { - for (let i = 0; i < this._length; i++) { - const property = this[i]; - if (borderProperties.has(property)) { - const value = this.getPropertyValue(property); - const longhandPriority = this._priorities.get(property) ?? ""; - let priority = longhandPriority; - if (prop === property && typeof prior === "string") { - priority = prior; - } - properties.set(property, { property, value, priority }); - } - } - } - const parsedProperties = prepareBorderProperties(prop, val, prior, properties, { - globalObject: this._global - }); - for (const [property, item] of parsedProperties) { - const { priority, value } = item; - this._setProperty(property, value, priority); - } - }, - enumerable: false - }, + get parentRule() { + return this._parentRule; + } - _flexBoxSetter: { - /** - * @param {string} prop - * @param {string} val - * @param {string} prior - * @param {string} shorthandProperty - */ - value(prop, val, prior, shorthandProperty) { - if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { - return; - } - const shorthandPriority = this._priorities.get(shorthandProperty); - this.removeProperty(shorthandProperty); - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - this.removeProperty(prop); - if (shorthandPriority && priority) { - this._setProperty(prop, val); - } else { - this._setProperty(prop, val, priority); - } - if (val && !hasVarFunc(val)) { - const longhandValues = []; - const shorthandItem = shorthandProperties.get(shorthandProperty); - let hasGlobalKeyword = false; - for (const [longhandProperty] of shorthandItem.shorthandFor) { - if (longhandProperty === prop) { - if (isGlobalKeyword(val)) { - hasGlobalKeyword = true; - } - longhandValues.push(val); - } else { - const longhandValue = this.getPropertyValue(longhandProperty); - const longhandPriority = this._priorities.get(longhandProperty) ?? ""; - if (!longhandValue || longhandPriority !== priority) { - break; - } - if (isGlobalKeyword(longhandValue)) { - hasGlobalKeyword = true; - } - longhandValues.push(longhandValue); + /** + * Non-standard. + * To resolve getComputedStyle(), we need to setup additional option values. + * @param {object} opt - Options + * @returns {void} + */ + setOptions(opt = {}) { + for (const [key, value] of Object.entries(opt)) { + switch (key) { + case "format": { + if (value === "computedValue") { + this._computed = true; + this._options[key] = value; } + break; } - if (longhandValues.length === shorthandItem.shorthandFor.size) { - if (hasGlobalKeyword) { - const [firstValue, ...restValues] = longhandValues; - if (restValues.every((value) => value === firstValue)) { - this._setProperty(shorthandProperty, firstValue, priority); - } - } else { - const parsedValue = shorthandItem.parse(longhandValues.join(" ")); - const shorthandValue = Object.values(parsedValue).join(" "); - this._setProperty(shorthandProperty, shorthandValue, priority); - } + case "readOnly": { + this._readonly = value === true; + break; + } + default: { + this._options[key] = value; } } - }, - enumerable: false - }, + } + } - _positionShorthandSetter: { - /** - * @param {string} prop - * @param {Array|string} val - * @param {string} prior - */ - value(prop, val, prior) { - if (!shorthandProperties.has(prop)) { - return; - } - const shorthandValues = []; - if (Array.isArray(val)) { - shorthandValues.push(...val); - } else if (typeof val === "string") { - shorthandValues.push(val); - } else { - return; + // Internal methods + /** + * @param {string} property + * @param {string} val + * @param {string} priority + */ + _setProperty(property, val, priority) { + if (typeof val !== "string") { + return; + } + if (val === "") { + this.removeProperty(property); + return; + } + let originalText = ""; + if (typeof this._onChange === "function") { + originalText = this.cssText; + } + if (this._values.has(property)) { + const index = Array.prototype.indexOf.call(this, property); + // The property already exists but is not indexed into `this` so add it. + if (index < 0) { + this[this._length] = property; + this._length++; } + } else { + // New property. + this[this._length] = property; + this._length++; + } + if (priority === "important") { + this._priorities.set(property, priority); + } else { + this._priorities.delete(property); + } + this._values.set(property, val); + if (typeof this._onChange === "function" && this.cssText !== originalText && !this._updating) { + this._onChange(this.cssText); + } + } + + /** + * @param {string} prop + * @param {object|Array|string} val + * @param {string} prior + */ + _borderSetter(prop, val, prior) { + const properties = new Map(); + if (prop === "border") { let priority = ""; if (typeof prior === "string") { priority = prior; } else { priority = this._priorities.get(prop) ?? ""; } - const { position, shorthandFor } = shorthandProperties.get(prop); - let hasPriority = false; - for (const [longhandProperty, longhandItem] of shorthandFor) { - const { position: longhandPosition } = longhandItem; - const longhandValue = getPositionValue(shorthandValues, longhandPosition); - if (priority) { - this._setProperty(longhandProperty, longhandValue, priority); - } else { - const longhandPriority = this._priorities.get(longhandProperty) ?? ""; - if (longhandPriority) { - hasPriority = true; - } else { - this._setProperty(longhandProperty, longhandValue, priority); + properties.set(prop, { propery: prop, value: val, priority }); + } else { + for (let i = 0; i < this._length; i++) { + const property = this[i]; + if (borderProperties.has(property)) { + const value = this.getPropertyValue(property); + const longhandPriority = this._priorities.get(property) ?? ""; + let priority = longhandPriority; + if (prop === property && typeof prior === "string") { + priority = prior; } + properties.set(property, { property, value, priority }); } } - if (hasPriority) { - this.removeProperty(prop); - } else { - const shorthandValue = getPositionValue(shorthandValues, position); - this._setProperty(prop, shorthandValue, priority); - } - }, - enumerable: false - }, + } + const parsedProperties = prepareBorderProperties(prop, val, prior, properties, { + globalObject: this._global, + options: this._options + }); + for (const [property, item] of parsedProperties) { + const { priority, value } = item; + this._setProperty(property, value, priority); + } + } - _positionLonghandSetter: { - /** - * @param {string} prop - * @param {string} val - * @param {string} prior - * @param {string} shorthandProperty - */ - value(prop, val, prior, shorthandProperty) { - if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { - return; - } - const shorthandPriority = this._priorities.get(shorthandProperty); - this.removeProperty(shorthandProperty); - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - this.removeProperty(prop); - if (shorthandPriority && priority) { - this._setProperty(prop, val); - } else { - this._setProperty(prop, val, priority); - } - if (val && !hasVarFunc(val)) { - const longhandValues = []; - const { shorthandFor, position: shorthandPosition } = - shorthandProperties.get(shorthandProperty); - for (const [longhandProperty] of shorthandFor) { + /** + * @param {string} prop + * @param {string} val + * @param {string} prior + * @param {string} shorthandProperty + */ + _flexBoxSetter(prop, val, prior, shorthandProperty) { + if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { + return; + } + const shorthandPriority = this._priorities.get(shorthandProperty); + this.removeProperty(shorthandProperty); + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = this._priorities.get(prop) ?? ""; + } + this.removeProperty(prop); + if (shorthandPriority && priority) { + this._setProperty(prop, val); + } else { + this._setProperty(prop, val, priority); + } + if (val && !hasVarFunc(val)) { + const longhandValues = []; + const shorthandItem = shorthandProperties.get(shorthandProperty); + let hasGlobalKeyword = false; + for (const [longhandProperty] of shorthandItem.shorthandFor) { + if (longhandProperty === prop) { + if (isGlobalKeyword(val)) { + hasGlobalKeyword = true; + } + longhandValues.push(val); + } else { const longhandValue = this.getPropertyValue(longhandProperty); const longhandPriority = this._priorities.get(longhandProperty) ?? ""; if (!longhandValue || longhandPriority !== priority) { - return; + break; + } + if (isGlobalKeyword(longhandValue)) { + hasGlobalKeyword = true; } longhandValues.push(longhandValue); } - if (longhandValues.length === shorthandFor.size) { - const replacedValue = getPositionValue(longhandValues, shorthandPosition); - this._setProperty(shorthandProperty, replacedValue); + } + if (longhandValues.length === shorthandItem.shorthandFor.size) { + if (hasGlobalKeyword) { + const [firstValue, ...restValues] = longhandValues; + if (restValues.every((value) => value === firstValue)) { + this._setProperty(shorthandProperty, firstValue, priority); + } + } else { + const parsedValue = shorthandItem.parse(longhandValues.join(" ")); + if (parsedValue) { + const shorthandValue = Object.values(parsedValue).join(" "); + this._setProperty(shorthandProperty, shorthandValue, priority); + } } } - }, - enumerable: false + } } -}); -// Properties -Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties); + /** + * @param {string} prop + * @param {Array|string} val + * @param {string} prior + */ + _positionShorthandSetter(prop, val, prior) { + if (!shorthandProperties.has(prop)) { + return; + } + const shorthandValues = []; + if (Array.isArray(val)) { + shorthandValues.push(...val); + } else if (typeof val === "string") { + shorthandValues.push(val); + } else { + return; + } + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = this._priorities.get(prop) ?? ""; + } + const { position, shorthandFor } = shorthandProperties.get(prop); + let hasPriority = false; + for (const [longhandProperty, longhandItem] of shorthandFor) { + const { position: longhandPosition } = longhandItem; + const longhandValue = getPositionValue(shorthandValues, longhandPosition); + if (priority) { + this._setProperty(longhandProperty, longhandValue, priority); + } else { + const longhandPriority = this._priorities.get(longhandProperty) ?? ""; + if (longhandPriority) { + hasPriority = true; + } else { + this._setProperty(longhandProperty, longhandValue, priority); + } + } + } + if (hasPriority) { + this.removeProperty(prop); + } else { + const shorthandValue = getPositionValue(shorthandValues, position); + this._setProperty(prop, shorthandValue, priority); + } + } -// Additional properties -[...allProperties, ...allExtraProperties].forEach((property) => { - if (!implementedProperties.has(property)) { - const declaration = getPropertyDescriptor(property); - Object.defineProperty(CSSStyleDeclaration.prototype, property, declaration); - const camel = dashedToCamelCase(property); - Object.defineProperty(CSSStyleDeclaration.prototype, camel, declaration); - if (/^webkit[A-Z]/.test(camel)) { - const pascal = camel.replace(/^webkit/, "Webkit"); - Object.defineProperty(CSSStyleDeclaration.prototype, pascal, declaration); + /** + * @param {string} prop + * @param {string} val + * @param {string} prior + * @param {string} shorthandProperty + */ + _positionLonghandSetter(prop, val, prior, shorthandProperty) { + if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { + return; + } + const shorthandPriority = this._priorities.get(shorthandProperty); + this.removeProperty(shorthandProperty); + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = this._priorities.get(prop) ?? ""; + } + this.removeProperty(prop); + if (shorthandPriority && priority) { + this._setProperty(prop, val); + } else { + this._setProperty(prop, val, priority); + } + if (val && !hasVarFunc(val)) { + const longhandValues = []; + const { shorthandFor, position: shorthandPosition } = + shorthandProperties.get(shorthandProperty); + for (const [longhandProperty] of shorthandFor) { + const longhandValue = this.getPropertyValue(longhandProperty); + const longhandPriority = this._priorities.get(longhandProperty) ?? ""; + if (!longhandValue || longhandPriority !== priority) { + return; + } + longhandValues.push(longhandValue); + } + if (longhandValues.length === shorthandFor.size) { + const replacedValue = getPositionValue(longhandValues, shorthandPosition); + this._setProperty(shorthandProperty, replacedValue, priority); + } } } -}); +} module.exports = { - CSSStyleDeclaration, - propertyList: Object.fromEntries(implementedProperties) + CSSStyleDeclaration }; diff --git a/lib/CSSStyleProperties.js b/lib/CSSStyleProperties.js new file mode 100644 index 00000000..d693ccfe --- /dev/null +++ b/lib/CSSStyleProperties.js @@ -0,0 +1,38 @@ +"use strict"; + +const { CSSStyleDeclaration } = require("./CSSStyleDeclaration"); +const generatedProperties = require("./generated/properties"); +const propertyList = require("./generated/propertyList"); +const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); + +/** + * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface + */ +class CSSStyleProperties extends CSSStyleDeclaration { + get cssFloat() { + return this.getPropertyValue("float"); + } + + set cssFloat(value) { + this.setProperty("float", value); + } +} + +// Properties +Object.defineProperties(CSSStyleProperties.prototype, generatedProperties); + +// Additional properties +for (const definition of propertyList.values()) { + const { legacyAliasOf, name, styleDeclaration } = definition; + const property = legacyAliasOf ?? name; + if (!Object.hasOwn(generatedProperties, property)) { + const declaration = getPropertyDescriptor(property); + for (const aliasProperty of styleDeclaration) { + Object.defineProperty(CSSStyleProperties.prototype, aliasProperty, declaration); + } + } +} + +module.exports = { + CSSStyleProperties +}; diff --git a/lib/generated/allProperties.js b/lib/generated/allProperties.js index a7a8c6ce..ac7b6aba 100644 --- a/lib/generated/allProperties.js +++ b/lib/generated/allProperties.js @@ -1,5 +1,5 @@ "use strict"; -// autogenerated - 2025-08-02 +// autogenerated - 2025-09-23 // https://www.w3.org/Style/CSS/all-properties.en.html module.exports = new Set([ @@ -310,7 +310,7 @@ module.exports = new Set([ "item-direction", "item-flow", "item-pack", - "item-slack", + "item-tolerance", "item-track", "item-wrap", "justify-content", diff --git a/lib/generated/implementedProperties.js b/lib/generated/implementedProperties.js new file mode 100644 index 00000000..59aaab1a --- /dev/null +++ b/lib/generated/implementedProperties.js @@ -0,0 +1,313 @@ +"use strict"; +// autogenerated - 2025-11-01 + +module.exports = new Map([ + [ + "-webkit-border-after-color", + null + ], + [ + "-webkit-border-before-color", + null + ], + [ + "-webkit-border-end-color", + null + ], + [ + "-webkit-border-start-color", + null + ], + [ + "-webkit-column-rule-color", + null + ], + [ + "-webkit-tap-highlight-color", + null + ], + [ + "-webkit-text-emphasis-color", + null + ], + [ + "-webkit-text-fill-color", + null + ], + [ + "-webkit-text-stroke-color", + null + ], + [ + "background", + null + ], + [ + "background-attachment", + null + ], + [ + "background-clip", + null + ], + [ + "background-color", + null + ], + [ + "background-image", + null + ], + [ + "background-origin", + null + ], + [ + "background-position", + null + ], + [ + "background-repeat", + null + ], + [ + "background-size", + null + ], + [ + "border", + null + ], + [ + "border-bottom", + null + ], + [ + "border-bottom-color", + null + ], + [ + "border-bottom-style", + null + ], + [ + "border-bottom-width", + null + ], + [ + "border-collapse", + null + ], + [ + "border-color", + null + ], + [ + "border-left", + null + ], + [ + "border-left-color", + null + ], + [ + "border-left-style", + null + ], + [ + "border-left-width", + null + ], + [ + "border-right", + null + ], + [ + "border-right-color", + null + ], + [ + "border-right-style", + null + ], + [ + "border-right-width", + null + ], + [ + "border-spacing", + null + ], + [ + "border-style", + null + ], + [ + "border-top", + null + ], + [ + "border-top-color", + null + ], + [ + "border-top-style", + null + ], + [ + "border-top-width", + null + ], + [ + "border-width", + null + ], + [ + "bottom", + null + ], + [ + "clear", + null + ], + [ + "clip", + null + ], + [ + "color", + null + ], + [ + "display", + null + ], + [ + "flex", + null + ], + [ + "flex-basis", + null + ], + [ + "flex-grow", + null + ], + [ + "flex-shrink", + null + ], + [ + "float", + null + ], + [ + "flood-color", + null + ], + [ + "font", + null + ], + [ + "font-family", + null + ], + [ + "font-size", + null + ], + [ + "font-style", + null + ], + [ + "font-variant", + null + ], + [ + "font-weight", + null + ], + [ + "height", + null + ], + [ + "left", + null + ], + [ + "lighting-color", + null + ], + [ + "line-height", + null + ], + [ + "margin", + null + ], + [ + "margin-bottom", + null + ], + [ + "margin-left", + null + ], + [ + "margin-right", + null + ], + [ + "margin-top", + null + ], + [ + "opacity", + null + ], + [ + "outline-color", + null + ], + [ + "padding", + null + ], + [ + "padding-bottom", + null + ], + [ + "padding-left", + null + ], + [ + "padding-right", + null + ], + [ + "padding-top", + null + ], + [ + "right", + null + ], + [ + "stop-color", + null + ], + [ + "top", + null + ], + [ + "width", + null + ] +]); 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..2078f0ba 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -1,6 +1,6 @@ "use strict"; -const implementedProperties = require("./generated/implementedProperties"); +const propertyList = require("./generated/propertyList"); const { hasVarFunc, isGlobalKeyword, isValidPropertyValue, splitValue } = require("./parsers"); const background = require("./properties/background"); const border = require("./properties/border"); @@ -16,38 +16,57 @@ const font = require("./properties/font"); const margin = require("./properties/margin"); const padding = require("./properties/padding"); -const borderImageProperty = "border-image"; +const BACKGROUND = "background"; +const BACKGROUND_COLOR = "background-color"; +const BACKGROUND_SIZE = "background-size"; +const BORDER = "border"; +const BORDER_BOTTOM = "border-bottom"; +const BORDER_COLOR = "border-color"; +const BORDER_IMAGE = "border-image"; +const BORDER_LEFT = "border-left"; +const BORDER_RIGHT = "border-right"; +const BORDER_STYLE = "border-style"; +const BORDER_TOP = "border-top"; +const BORDER_WIDTH = "border-width"; +const TOP = "top"; +const RIGHT = "right"; +const BOTTOM = "bottom"; +const LEFT = "left"; +const WIDTH = "width"; +const STYLE = "style"; +const COLOR = "color"; +const NONE = "none"; -exports.shorthandProperties = new Map([ - ["background", background], +const shorthandProperties = new Map([ + [BACKGROUND, background], [ - "border", + BORDER, { definition: border.definition, parse: border.parse, shorthandFor: new Map([ ...border.shorthandFor, ...border.positionShorthandFor, - [borderImageProperty, null] + [BORDER_IMAGE, null] ]) } ], - ["border-width", borderWidth], - ["border-style", borderStyle], - ["border-color", borderColor], - ["border-top", borderTop], - ["border-right", borderRight], - ["border-bottom", borderBottom], - ["border-left", borderLeft], + [BORDER_WIDTH, borderWidth], + [BORDER_STYLE, borderStyle], + [BORDER_COLOR, borderColor], + [BORDER_TOP, borderTop], + [BORDER_RIGHT, borderRight], + [BORDER_BOTTOM, borderBottom], + [BORDER_LEFT, borderLeft], ["flex", flex], ["font", font], ["margin", margin], ["padding", padding] ]); -exports.borderProperties = new Set([ - "border", - borderImageProperty, +const borderProperties = new Set([ + BORDER, + BORDER_IMAGE, ...border.shorthandFor.keys(), ...border.positionShorthandFor.keys(), ...borderTop.shorthandFor.keys(), @@ -56,7 +75,7 @@ exports.borderProperties = new Set([ ...borderLeft.shorthandFor.keys() ]); -exports.getPositionValue = (positionValues, position) => { +const getPositionValue = (positionValues, position) => { switch (positionValues.length) { case 1: { const [val1] = positionValues; @@ -65,16 +84,16 @@ exports.getPositionValue = (positionValues, position) => { case 2: { const [val1, val2] = positionValues; switch (position) { - case "top": { + case TOP: { return val1; } - case "right": { + case RIGHT: { return val2; } - case "bottom": { + case BOTTOM: { return val1; } - case "left": { + case LEFT: { return val2; } default: { @@ -88,16 +107,16 @@ exports.getPositionValue = (positionValues, position) => { case 3: { const [val1, val2, val3] = positionValues; switch (position) { - case "top": { + case TOP: { return val1; } - case "right": { + case RIGHT: { return val2; } - case "bottom": { + case BOTTOM: { return val3; } - case "left": { + case LEFT: { return val2; } default: { @@ -114,16 +133,16 @@ exports.getPositionValue = (positionValues, position) => { case 4: { const [val1, val2, val3, val4] = positionValues; switch (position) { - case "top": { + case TOP: { return val1; } - case "right": { + case RIGHT: { return val2; } - case "bottom": { + case BOTTOM: { return val3; } - case "left": { + case LEFT: { return val4; } default: { @@ -144,12 +163,6 @@ exports.getPositionValue = (positionValues, position) => { } }; -const borderElements = { - name: "border", - positions: ["top", "right", "bottom", "left"], - lines: ["width", "style", "color"] -}; - const getPropertyItem = (property, properties) => { const propertyItem = properties.get(property) ?? { property, @@ -159,10 +172,52 @@ const getPropertyItem = (property, properties) => { return propertyItem; }; +const replaceBackgroundShorthand = (property, properties, opt) => { + const { value: propertyValue } = properties.get(property); + const parsedValue = background.shorthandFor.get(property).parse(propertyValue, opt); + const values = splitValue(parsedValue, { + delimiter: "," + }); + const { value: shorthandValue } = properties.get(BACKGROUND); + const bgValues = background.parse(shorthandValue, opt); + const bgLength = bgValues.length; + if (property === BACKGROUND_COLOR) { + bgValues[bgLength - 1][property] = parsedValue[0]; + } else { + for (let i = 0; i < bgLength; i++) { + bgValues[i][property] = values[i]; + } + } + const backgrounds = []; + for (const bgValue of bgValues) { + const bg = []; + for (const [longhand, value] of Object.entries(bgValue)) { + if (value) { + if (value !== background.initialValues.get(longhand)) { + if (longhand === BACKGROUND_SIZE) { + bg.push(`/ ${value}`); + } else { + bg.push(value); + } + } + } + } + backgrounds.push(bg.join(" ")); + } + return backgrounds.join(", "); +}; + +const borderElements = { + name: BORDER, + positions: [TOP, RIGHT, BOTTOM, LEFT], + lines: [WIDTH, STYLE, COLOR] +}; + 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 +226,16 @@ const matchesBorderShorthandValue = (property, value, shorthandValue, opt = {}) }; const replaceBorderShorthandValue = (value, shorthandValue, opt = {}) => { - const { globalObject } = opt; + const { globalObject, options } = opt; const borderFirstInitialKey = border.initialValues.keys().next().value; const borderFirstInitialValue = border.initialValues.get(borderFirstInitialKey); - const valueObj = border.parse(value, { - globalObject - }); + const parseOpt = { + globalObject, + options + }; + const valueObj = border.parse(value, parseOpt); const shorthandObj = shorthandValue - ? border.parse(shorthandValue, { - globalObject - }) + ? border.parse(shorthandValue, parseOpt) : { [borderFirstInitialKey]: borderFirstInitialValue }; @@ -220,16 +275,16 @@ const replacePositionValue = (value, positionValues, position) => { return positionValues.join(" "); } switch (position) { - case "top": { + case TOP: { return [value, val1, val1].join(" "); } - case "right": { + case RIGHT: { return [val1, value, val1, val1].join(" "); } - case "bottom": { + case BOTTOM: { return [val1, val1, value].join(" "); } - case "left": { + case LEFT: { return [val1, val1, val1, value].join(" "); } default: @@ -242,25 +297,25 @@ const replacePositionValue = (value, positionValues, position) => { return replacePositionValue(value, [val1], position); } switch (position) { - case "top": { + case TOP: { if (val1 === value) { return positionValues.join(" "); } return [value, val2, val1].join(" "); } - case "right": { + case RIGHT: { if (val2 === value) { return positionValues.join(" "); } return [val1, value, val1, val2].join(" "); } - case "bottom": { + case BOTTOM: { if (val1 === value) { return positionValues.join(" "); } return [val1, val2, value].join(" "); } - case "left": { + case LEFT: { if (val2 === value) { return positionValues.join(" "); } @@ -276,7 +331,7 @@ const replacePositionValue = (value, positionValues, position) => { return replacePositionValue(value, [val1, val2], position); } switch (position) { - case "top": { + case TOP: { if (val1 === value) { return positionValues.join(" "); } else if (val3 === value) { @@ -284,13 +339,13 @@ const replacePositionValue = (value, positionValues, position) => { } return [value, val2, val3].join(" "); } - case "right": { + case RIGHT: { if (val2 === value) { return positionValues.join(" "); } return [val1, value, val3, val2].join(" "); } - case "bottom": { + case BOTTOM: { if (val3 === value) { return positionValues.join(" "); } else if (val1 === value) { @@ -298,7 +353,7 @@ const replacePositionValue = (value, positionValues, position) => { } return [val1, val2, value].join(" "); } - case "left": { + case LEFT: { if (val2 === value) { return positionValues.join(" "); } @@ -314,13 +369,13 @@ const replacePositionValue = (value, positionValues, position) => { return replacePositionValue(value, [val1, val2, val3], position); } switch (position) { - case "top": { + case TOP: { if (val1 === value) { return positionValues.join(" "); } return [value, val2, val3, val4].join(" "); } - case "right": { + case RIGHT: { if (val2 === value) { return positionValues.join(" "); } else if (val4 === value) { @@ -328,13 +383,13 @@ const replacePositionValue = (value, positionValues, position) => { } return [val1, value, val3, val4].join(" "); } - case "bottom": { + case BOTTOM: { if (val3 === value) { return positionValues.join(" "); } return [val1, val2, value, val4].join(" "); } - case "left": { + case LEFT: { if (val4 === value) { return positionValues.join(" "); } else if (val2 === value) { @@ -350,11 +405,15 @@ const replacePositionValue = (value, positionValues, position) => { } }; -exports.prepareBorderProperties = (property, value, priority, properties, opt = {}) => { +const prepareBorderProperties = (property, value, priority, properties, opt = {}) => { if (typeof property !== "string" || value === null) { return; } - const { globalObject } = opt; + const { globalObject, options } = opt; + const parseOpt = { + globalObject, + options + }; const { lines, name, positions } = borderElements; const [prop1, prop2, prop3] = property.split("-"); if (prop1 !== name) { @@ -374,10 +433,10 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const nameProperty = prop1; // Empty string, global keywords, var(), value of longhands. if (typeof value === "string") { - // longhand properties + // Handle longhand properties if (prop3) { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineProperty = `${prop1}-${prop3}`; const lineItem = getPropertyItem(lineProperty, properties); const positionProperty = `${prop1}-${prop2}`; @@ -404,9 +463,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } else { if ( nameItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, nameItem.value, { - globalObject - }) + !matchesBorderShorthandValue(lineProperty, propertyValue, nameItem.value, parseOpt) ) { nameItem.value = ""; } @@ -415,22 +472,20 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } if ( positionItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, { - globalObject - }) + !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, parseOpt) ) { positionItem.value = ""; } } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); borderItems.set(positionProperty, positionItem); borderItems.set(longhandProperty, longhandItem); - // border-top, border-right, border-bottom, border-left shorthands + // Handle border-top, border-right, border-bottom, border-left shorthands } else if (prop2 && positions.includes(prop2)) { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineWidthProperty = `${prop1}-width`; const lineWidthItem = getPropertyItem(lineWidthProperty, properties); const lineStyleProperty = `${prop1}-style`; @@ -463,19 +518,26 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } else { if ( nameItem.value && - !matchesBorderShorthandValue(property, propertyValue, nameItem.value, { - globalObject - }) + !matchesBorderShorthandValue(property, propertyValue, nameItem.value, parseOpt) ) { nameItem.value = ""; } - if (lineWidthItem.value && isValidPropertyValue(lineWidthProperty, propertyValue)) { + if ( + lineWidthItem.value && + isValidPropertyValue(lineWidthProperty, propertyValue, globalObject) + ) { lineWidthItem.value = propertyValue; } - if (lineStyleItem.value && isValidPropertyValue(lineStyleProperty, propertyValue)) { + if ( + lineStyleItem.value && + isValidPropertyValue(lineStyleProperty, propertyValue, globalObject) + ) { lineStyleItem.value = propertyValue; } - if (lineColorItem.value && isValidPropertyValue(lineColorProperty, propertyValue)) { + if ( + lineColorItem.value && + isValidPropertyValue(lineColorProperty, propertyValue, globalObject) + ) { lineColorItem.value = propertyValue; } } @@ -487,15 +549,15 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineWidthProperty, lineWidthItem); borderItems.set(lineStyleProperty, lineStyleItem); borderItems.set(lineColorProperty, lineColorItem); borderItems.set(positionProperty, positionItem); - // border-width, border-style, border-color + // Handle border-width, border-style, border-color shorthands } else if (prop2 && lines.includes(prop2)) { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineProperty = `${prop1}-${prop2}`; const lineItem = getPropertyItem(lineProperty, properties); lineItem.value = value; @@ -514,9 +576,11 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const longhandProperty = `${prop1}-${position}-${prop2}`; const longhandItem = getPropertyItem(longhandProperty, properties); if (propertyValue) { - positionItem.value = replaceBorderShorthandValue(propertyValue, positionItem.value, { - globalObject - }); + positionItem.value = replaceBorderShorthandValue( + propertyValue, + positionItem.value, + parseOpt + ); } else { positionItem.value = ""; } @@ -526,14 +590,14 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); - // border shorthand + // Handle border shorthand } else { const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const propertyValue = hasVarFunc(value) ? "" : value; - imageItem.value = propertyValue ? "none" : ""; + imageItem.value = propertyValue ? NONE : ""; for (const line of lines) { const lineProperty = `${prop1}-${line}`; const lineItem = getPropertyItem(lineProperty, properties); @@ -556,7 +620,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = } } borderItems.set(property, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); } // Values of border-width, border-style, border-color } else if (Array.isArray(value)) { @@ -564,15 +628,17 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = return; } const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineProperty = `${prop1}-${prop2}`; const lineItem = getPropertyItem(lineProperty, properties); if (value.length === 1) { const [propertyValue] = value; - if (nameItem.value && propertyValue) { - nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, { - globalObject - }); + if (nameItem.value) { + if (hasVarFunc(nameItem.value)) { + nameItem.value = ""; + } else if (propertyValue) { + nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, parseOpt); + } } } else { nameItem.value = ""; @@ -624,9 +690,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = positionItem.value = replaceBorderShorthandValue( positionValues[position], positionItem.value, - { - globalObject - } + parseOpt ); } const longhandProperty = `${positionProperty}-${prop2}`; @@ -637,17 +701,17 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineProperty, lineItem); // Values of border, border-top, border-right, border-bottom, border-top. } else if (value && typeof value === "object") { - // position shorthands + // Handle position shorthands if (prop2) { if (!positions.includes(prop2)) { return; } const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineWidthProperty = `${prop1}-width`; const lineWidthItem = getPropertyItem(lineWidthProperty, properties); const lineStyleProperty = `${prop1}-style`; @@ -658,11 +722,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const positionItem = getPropertyItem(positionProperty, properties); if (nameItem.value) { for (const positionValue of Object.values(value)) { - if ( - !matchesBorderShorthandValue(property, positionValue, nameItem.value, { - globalObject - }) - ) { + if (!matchesBorderShorthandValue(property, positionValue, nameItem.value, parseOpt)) { nameItem.value = ""; break; } @@ -675,7 +735,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const longhandItem = getPropertyItem(longhandProperty, properties); if (Object.hasOwn(value, longhandProperty)) { const itemValue = value[longhandProperty]; - if (line === "width") { + if (line === WIDTH) { if (lineWidthItem.value) { lineWidthItem.value = replacePositionValue( itemValue, @@ -683,7 +743,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "style") { + } else if (line === STYLE) { if (lineStyleItem.value) { lineStyleItem.value = replacePositionValue( itemValue, @@ -691,7 +751,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "color") { + } else if (line === COLOR) { if (lineColorItem.value) { lineColorItem.value = replacePositionValue( itemValue, @@ -704,7 +764,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = longhandItem.priority = priority; } else { const itemValue = border.initialValues.get(`${prop1}-${line}`); - if (line === "width") { + if (line === WIDTH) { if (lineWidthItem.value) { lineWidthItem.value = replacePositionValue( itemValue, @@ -712,7 +772,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "style") { + } else if (line === STYLE) { if (lineStyleItem.value) { lineStyleItem.value = replacePositionValue( itemValue, @@ -720,7 +780,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = prop2 ); } - } else if (line === "color") { + } else if (line === COLOR) { if (lineColorItem.value) { lineColorItem.value = replacePositionValue( itemValue, @@ -735,15 +795,15 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineWidthProperty, lineWidthItem); borderItems.set(lineStyleProperty, lineStyleItem); borderItems.set(lineColorProperty, lineColorItem); borderItems.set(positionProperty, positionItem); - // border shorthand + // Handle border shorthand } else { const nameItem = getPropertyItem(prop1, properties); - const imageItem = getPropertyItem(borderImageProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); const lineWidthProperty = `${prop1}-width`; const lineWidthItem = getPropertyItem(lineWidthProperty, properties); const lineStyleProperty = `${prop1}-style`; @@ -753,7 +813,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = const propertyValue = Object.values(value).join(" "); nameItem.value = propertyValue; nameItem.priority = priority; - imageItem.value = propertyValue ? "none" : ""; + imageItem.value = propertyValue ? NONE : ""; if (Object.hasOwn(value, lineWidthProperty)) { lineWidthItem.value = value[lineWidthProperty]; } else { @@ -792,7 +852,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = borderItems.set(positionProperty, positionItem); } borderItems.set(property, nameItem); - borderItems.set(borderImageProperty, imageItem); + borderItems.set(BORDER_IMAGE, imageItem); borderItems.set(lineWidthProperty, lineWidthItem); borderItems.set(lineStyleProperty, lineStyleItem); borderItems.set(lineColorProperty, lineColorItem); @@ -803,7 +863,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 +872,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 +882,7 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(positionProperty, positionItem); + borderProps.set(positionProperty, positionItem); for (const line of lines) { const longhandProperty = `${name}-${position}-${line}`; const longhandItem = borderItems.get(longhandProperty) ?? @@ -831,16 +891,16 @@ exports.prepareBorderProperties = (property, value, priority, properties, opt = value: "", priority: "" }; - borderProperties.set(longhandProperty, longhandItem); + borderProps.set(longhandProperty, longhandItem); } } - const borderImageItem = borderItems.get(borderImageProperty) ?? { - property: borderImageProperty, + const borderImageItem = borderItems.get(BORDER_IMAGE) ?? { + property: BORDER_IMAGE, value: "", priority: "" }; - borderProperties.set(borderImageProperty, borderImageItem); - return borderProperties; + borderProps.set(BORDER_IMAGE, borderImageItem); + return borderProps; }; const generateBorderLineShorthand = (items, property, prior) => { @@ -849,7 +909,7 @@ const generateBorderLineShorthand = (items, property, prior) => { const { value: itemValue } = item; values.push(itemValue); } - const value = exports.getPositionValue(values); + const value = getPositionValue(values); const priority = prior ? prior : ""; return [property, { property, value, priority }]; }; @@ -892,7 +952,7 @@ const prepareBorderShorthands = (properties) => { for (const [property, { priority, value }] of properties) { const [, positionPart, linePart] = property.split("-"); switch (linePart) { - case "width": { + case WIDTH: { if (priority) { lineWidthPriorItems.set(property, { property, value, priority }); } else { @@ -900,7 +960,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "style": { + case STYLE: { if (priority) { lineStylePriorItems.set(property, { property, value, priority }); } else { @@ -908,7 +968,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "color": { + case COLOR: { if (priority) { lineColorPriorItems.set(property, { property, value, priority }); } else { @@ -919,7 +979,7 @@ const prepareBorderShorthands = (properties) => { default: } switch (positionPart) { - case "top": { + case TOP: { if (priority) { positionTopPriorItems.set(property, { property, value, priority }); } else { @@ -927,7 +987,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "right": { + case RIGHT: { if (priority) { positionRightPriorItems.set(property, { property, value, priority }); } else { @@ -935,7 +995,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "bottom": { + case BOTTOM: { if (priority) { positionBottomPriorItems.set(property, { property, value, priority }); } else { @@ -943,7 +1003,7 @@ const prepareBorderShorthands = (properties) => { } break; } - case "left": { + case LEFT: { if (priority) { positionLeftPriorItems.set(property, { property, value, priority }); } else { @@ -955,37 +1015,37 @@ const prepareBorderShorthands = (properties) => { } } if (lineWidthItems.size === 4) { - const [property, item] = generateBorderLineShorthand(lineWidthItems, "border-width") ?? []; + const [property, item] = generateBorderLineShorthand(lineWidthItems, BORDER_WIDTH) ?? []; if (property && item) { properties.set(property, item); } } else if (lineWidthPriorItems.size === 4) { const [property, item] = - generateBorderLineShorthand(lineWidthPriorItems, "border-width", "important") ?? []; + generateBorderLineShorthand(lineWidthPriorItems, BORDER_WIDTH, "important") ?? []; if (property && item) { properties.set(property, item); } } if (lineStyleItems.size === 4) { - const [property, item] = generateBorderLineShorthand(lineStyleItems, "border-style") ?? []; + const [property, item] = generateBorderLineShorthand(lineStyleItems, BORDER_STYLE) ?? []; if (property && item) { properties.set(property, item); } } else if (lineStylePriorItems.size === 4) { const [property, item] = - generateBorderLineShorthand(lineStylePriorItems, "border-style", "important") ?? []; + generateBorderLineShorthand(lineStylePriorItems, BORDER_STYLE, "important") ?? []; if (property && item) { properties.set(property, item); } } if (lineColorItems.size === 4) { - const [property, item] = generateBorderLineShorthand(lineColorItems, "border-color") ?? []; + const [property, item] = generateBorderLineShorthand(lineColorItems, BORDER_COLOR) ?? []; if (property && item) { properties.set(property, item); } } else if (lineColorPriorItems.size === 4) { const [property, item] = - generateBorderLineShorthand(lineColorPriorItems, "border-color", "important") ?? []; + generateBorderLineShorthand(lineColorPriorItems, BORDER_COLOR, "important") ?? []; if (property && item) { properties.set(property, item); } @@ -993,12 +1053,12 @@ const prepareBorderShorthands = (properties) => { const nameItems = []; const namePriorItems = []; if (positionTopItems.size === 3) { - const [property, item] = generateBorderPositionShorthand(positionTopItems, "border-top") ?? []; + const [property, item] = generateBorderPositionShorthand(positionTopItems, BORDER_TOP) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1006,12 +1066,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionTopPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionTopPriorItems, "border-top", "important") ?? []; + generateBorderPositionShorthand(positionTopPriorItems, BORDER_TOP, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; namePriorItems.push(itemValue); } @@ -1020,12 +1080,12 @@ const prepareBorderShorthands = (properties) => { } if (positionRightItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionRightItems, "border-right") ?? []; + generateBorderPositionShorthand(positionRightItems, BORDER_RIGHT) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1033,12 +1093,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionRightPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionRightPriorItems, "border-right", "important") ?? []; + generateBorderPositionShorthand(positionRightPriorItems, BORDER_RIGHT, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1047,12 +1107,12 @@ const prepareBorderShorthands = (properties) => { } if (positionBottomItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionBottomItems, "border-bottom") ?? []; + generateBorderPositionShorthand(positionBottomItems, BORDER_BOTTOM) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1060,12 +1120,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionBottomPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionBottomPriorItems, "border-bottom", "important") ?? []; + generateBorderPositionShorthand(positionBottomPriorItems, BORDER_BOTTOM, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1073,13 +1133,12 @@ const prepareBorderShorthands = (properties) => { } } if (positionLeftItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionLeftItems, "border-left") ?? []; + const [property, item] = generateBorderPositionShorthand(positionLeftItems, BORDER_LEFT) ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1087,12 +1146,12 @@ const prepareBorderShorthands = (properties) => { } } else if (positionLeftPriorItems.size === 3) { const [property, item] = - generateBorderPositionShorthand(positionLeftPriorItems, "border-left", "important") ?? []; + generateBorderPositionShorthand(positionLeftPriorItems, BORDER_LEFT, "important") ?? []; if (property && item) { properties.set(property, item); - if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { const { value: itemValue } = item; nameItems.push(itemValue); } @@ -1101,33 +1160,32 @@ const prepareBorderShorthands = (properties) => { } const mixedPriorities = nameItems.length && namePriorItems.length; const imageItem = { - property: borderImageProperty, - value: "none", + property: BORDER_IMAGE, + value: NONE, priority: "" }; if (nameItems.length === 4) { - const [property, item] = generateBorderNameShorthand(nameItems, "border") ?? []; + const [property, item] = generateBorderNameShorthand(nameItems, BORDER) ?? []; if (property && item) { properties.set(property, item); - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } } else if (namePriorItems.length === 4) { - const [property, item] = - generateBorderNameShorthand(namePriorItems, "border", "important") ?? []; + const [property, item] = generateBorderNameShorthand(namePriorItems, BORDER, "important") ?? []; if (property && item) { properties.set(property, item); - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } - } else if (properties.has(borderImageProperty)) { - const { value: imageValue } = properties.get(borderImageProperty); - if (imageValue === "none") { + } else if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { if (mixedPriorities) { - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } else { - properties.delete(borderImageProperty); + properties.delete(BORDER_IMAGE); } } } @@ -1149,25 +1207,30 @@ const prepareBorderShorthands = (properties) => { } return new Map([...items, ...priorItems]); } - if (properties.has(borderImageProperty)) { - properties.delete(borderImageProperty); - properties.set(borderImageProperty, imageItem); + if (properties.has(BORDER_IMAGE)) { + properties.delete(BORDER_IMAGE); + properties.set(BORDER_IMAGE, imageItem); } return properties; }; -exports.prepareProperties = (properties, opt = {}) => { - const { globalObject } = opt; +const prepareProperties = (properties, opt = {}) => { + const { globalObject, options } = opt; + const parseOpt = { + globalObject, + options + }; const { positions } = borderElements; const parsedProperties = new Map(); const prepareShorthands = new Map(); - const borderProperties = new Map(); + const borderProps = new Map(); + let hasPrecedingBackground = false; 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 +1249,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 +1265,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, @@ -1221,7 +1282,7 @@ exports.prepareProperties = (properties, opt = {}) => { }); } } - } else if (parsedValues) { + } else if (parsedValues && typeof parsedValues !== "string") { for (const longhandProperty of Object.keys(parsedValues)) { const longhandValue = parsedValues[longhandProperty]; parsedProperties.set(longhandProperty, { @@ -1232,15 +1293,36 @@ exports.prepareProperties = (properties, opt = {}) => { } } if (!omitShorthandProperty) { - parsedProperties.set(property, { property, value, priority }); + if (property === BACKGROUND) { + hasPrecedingBackground = true; + parsedProperties.set(property, { property, value, priority }); + } else { + parsedProperties.set(property, { property, value, priority }); + } } } else { parsedProperties.set(property, { property, value, priority }); + if (hasPrecedingBackground) { + const { value: shorthandValue, priority: shorthandPriority } = properties.get(BACKGROUND); + if ((!shorthandPriority || priority) && !hasVarFunc(shorthandValue)) { + const replacedShorthandValue = replaceBackgroundShorthand( + property, + parsedProperties, + parseOpt + ); + properties.delete(BACKGROUND); + properties.set(BACKGROUND, { + property: BACKGROUND, + value: replacedShorthandValue, + priority: shorthandPriority + }); + } + } } } 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 +1333,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 +1343,13 @@ exports.prepareProperties = (properties, opt = {}) => { } } } - if (borderProperties.size) { + if (borderProps.size) { const longhandProperties = new Map(); - for (const [property, item] of borderProperties) { - if (exports.shorthandProperties.has(property)) { + for (const [property, item] of borderProps) { + if (shorthandProperties.has(property)) { const { value, priority } = item; - if (property === "border") { - const lineItems = border.parse(value, { - globalObject - }); + if (property === BORDER) { + const lineItems = border.parse(value, parseOpt); for (const [key, initialValue] of border.initialValues) { if (!Object.hasOwn(lineItems, key)) { lineItems[key] = initialValue; @@ -1297,22 +1377,20 @@ exports.prepareProperties = (properties, opt = {}) => { } } if (value) { - longhandProperties.set(borderImageProperty, { - property: borderImageProperty, - value: "none", + longhandProperties.set(BORDER_IMAGE, { + property: BORDER_IMAGE, + value: NONE, priority }); } } else { - const shorthandItem = exports.shorthandProperties.get(property); - const parsedItem = shorthandItem.parse(value, { - globalObject - }); + const shorthandItem = shorthandProperties.get(property); + const parsedItem = shorthandItem.parse(value, parseOpt); if (Array.isArray(parsedItem)) { const [namePart, linePart] = property.split("-"); for (const position of positions) { const longhandProperty = `${namePart}-${position}-${linePart}`; - const longhandValue = exports.getPositionValue(parsedItem, position); + const longhandValue = getPositionValue(parsedItem, position); const longhandItem = { property: longhandProperty, value: longhandValue, @@ -1371,7 +1449,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 +1493,12 @@ exports.normalizeBorderProperties = (properties) => { } return properties; }; + +module.exports = { + borderProperties, + getPositionValue, + normalizeProperties, + prepareBorderProperties, + prepareProperties, + shorthandProperties +}; diff --git a/lib/parsers.js b/lib/parsers.js index 8527e321..3a062bc9 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -6,6 +6,7 @@ const { } = require("@asamuzakjp/css-color"); const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree"); const csstree = require("css-tree"); +const { getCache, setCache } = require("./utils/cache"); const { asciiLowercase } = require("./utils/strings"); // CSS global keywords @@ -73,7 +74,7 @@ const varContainedRegEx = /(?<=[*/\s(])var\(/; const cssTree = csstree.fork(syntaxes); // Prepare stringified value. -exports.prepareValue = (value, globalObject = globalThis) => { +const prepareValue = (value, globalObject = globalThis) => { // `null` is converted to an empty string. // @see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString if (value === null) { @@ -100,30 +101,27 @@ exports.prepareValue = (value, globalObject = globalThis) => { }; // Value is a global keyword. -exports.isGlobalKeyword = (val) => { +const isGlobalKeyword = (val) => { return GLOBAL_KEY.includes(asciiLowercase(val)); }; // Value starts with and/or contains CSS var() function. -exports.hasVarFunc = (val) => { +const hasVarFunc = (val) => { return varRegEx.test(val) || varContainedRegEx.test(val); }; // Value starts with and/or contains CSS calc() related functions. -exports.hasCalcFunc = (val) => { +const hasCalcFunc = (val) => { return calcRegEx.test(val) || calcContainedRegEx.test(val); }; -// Splits value into an array. -// @see https://github.com/asamuzaK/cssColor/blob/main/src/js/util.ts -exports.splitValue = splitValue; - // Parse CSS to AST. -exports.parseCSS = (val, opt, toObject = false) => { +const parseCSS = (val, opt = {}, toObject = false) => { + const { globalObject, options } = opt; if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val, globalObject); } - const ast = cssTree.parse(val, opt); + const ast = cssTree.parse(val, options); if (toObject) { return cssTree.toPlainObject(ast); } @@ -132,9 +130,9 @@ exports.parseCSS = (val, opt, toObject = false) => { // Value is a valid property value. // Returns `false` for custom property and/or var(). -exports.isValidPropertyValue = (prop, val) => { +const isValidPropertyValue = (prop, val, globalObject) => { if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val, globalObject); } if (val === "") { return true; @@ -150,8 +148,12 @@ exports.isValidPropertyValue = (prop, val) => { } let ast; try { - ast = exports.parseCSS(val, { - context: "value" + // TBD: use cache? + ast = parseCSS(val, { + globalObject, + options: { + context: "value" + } }); } catch { return false; @@ -160,15 +162,30 @@ exports.isValidPropertyValue = (prop, val) => { return error === null && matched !== null; }; +const defaultOptions = { + format: "specifiedValue" +}; + // Simplify / resolve math functions. -exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { +const resolveCalc = (val, opt = {}) => { + const { globalObject, options } = opt; if (typeof val !== "string") { - val = exports.prepareValue(val); + val = prepareValue(val, globalObject); } - if (val === "" || exports.hasVarFunc(val) || !exports.hasCalcFunc(val)) { + if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) { return val; } - const obj = exports.parseCSS(val, { context: "value" }, true); + // TBD: use cache? + const obj = parseCSS( + val, + { + globalObject, + options: { + context: "value" + } + }, + true + ); if (!obj?.children) { return; } @@ -180,9 +197,10 @@ exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { const value = cssTree .generate(item) .replace(/\)(?!\)|\s|,)/g, ") ") + .replace(/,(?!\s)/g, ", ") .trim(); if (calcNameRegEx.test(itemName)) { - const newValue = cssCalc(value, opt); + const newValue = cssCalc(value, options ?? defaultOptions); values.push(newValue); } else { values.push(value); @@ -197,15 +215,13 @@ exports.resolveCalc = (val, opt = { format: "specifiedValue" }) => { }; // Parse property value. Returns string or array of parsed object. -exports.parsePropertyValue = (prop, val, opt = {}) => { +const parsePropertyValue = (prop, val, opt = {}) => { const { caseSensitive, globalObject, inArray } = opt; - val = exports.prepareValue(val, globalObject); - if (val === "" || exports.hasVarFunc(val)) { + val = prepareValue(val, globalObject); + if (val === "" || hasVarFunc(val)) { return val; - } else if (exports.hasCalcFunc(val)) { - const calculatedValue = exports.resolveCalc(val, { - format: "specifiedValue" - }); + } else if (hasCalcFunc(val)) { + const calculatedValue = resolveCalc(val, opt); if (typeof calculatedValue !== "string") { return; } @@ -237,8 +253,19 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { return; } try { - const ast = exports.parseCSS(val, { - context: "value" + let cacheKey = ""; + if (inArray) { + cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`; + const cachedValues = getCache(cacheKey); + if (Array.isArray(cachedValues)) { + return cachedValues; + } + } + const ast = parseCSS(val, { + globalObject, + options: { + context: "value" + } }); const { error, matched } = cssTree.lexer.matchProperty(prop, ast); if (error || !matched) { @@ -285,7 +312,7 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { type: "Calc", name: "calc", isNumber: false, - value: `${asciiLowercase(itemValue)}`, + value: asciiLowercase(itemValue), raw }); } @@ -324,6 +351,9 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { } } } + if (cacheKey) { + setCache(cacheKey, parsedValues); + } return parsedValues; } } catch { @@ -333,7 +363,7 @@ exports.parsePropertyValue = (prop, val, opt = {}) => { }; // Parse . -exports.parseNumber = (val, opt = {}) => { +const parseNumber = (val, opt = {}) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "Number") { @@ -356,7 +386,7 @@ exports.parseNumber = (val, opt = {}) => { }; // Parse . -exports.parseLength = (val, opt = {}) => { +const parseLength = (val, opt = {}) => { const [item] = val; const { type, value, unit } = item ?? {}; if (type !== "Dimension" && !(type === "Number" && value === "0")) { @@ -378,12 +408,12 @@ exports.parseLength = (val, opt = {}) => { if (num === 0 && !unit) { return `${num}px`; } else if (unit) { - return `${num}${asciiLowercase(unit)}`; + return `${num}${unit}`; } }; // Parse . -exports.parsePercentage = (val, opt = {}) => { +const parsePercentage = (val, opt = {}) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "Percentage" && !(type === "Number" && value === "0")) { @@ -409,7 +439,7 @@ exports.parsePercentage = (val, opt = {}) => { }; // Parse . -exports.parseLengthPercentage = (val, opt = {}) => { +const parseLengthPercentage = (val, opt = {}) => { const [item] = val; const { type, value, unit } = item ?? {}; if (type !== "Dimension" && type !== "Percentage" && !(type === "Number" && value === "0")) { @@ -432,7 +462,7 @@ exports.parseLengthPercentage = (val, opt = {}) => { if (/deg|g?rad|turn/i.test(unit)) { return; } - return `${num}${asciiLowercase(unit)}`; + return `${num}${unit}`; } else if (type === "Percentage") { return `${num}%`; } else if (num === 0) { @@ -441,7 +471,7 @@ exports.parseLengthPercentage = (val, opt = {}) => { }; // Parse . -exports.parseAngle = (val) => { +const parseAngle = (val) => { const [item] = val; const { type, value, unit } = item ?? {}; if (type !== "Dimension" && !(type === "Number" && value === "0")) { @@ -452,14 +482,14 @@ exports.parseAngle = (val) => { if (!/^(?:deg|g?rad|turn)$/i.test(unit)) { return; } - return `${num}${asciiLowercase(unit)}`; + return `${num}${unit}`; } else if (num === 0) { return `${num}deg`; } }; // Parse . -exports.parseUrl = (val) => { +const parseURL = (val) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "Url") { @@ -470,7 +500,7 @@ exports.parseUrl = (val) => { }; // Parse . -exports.parseString = (val) => { +const parseString = (val) => { const [item] = val; const { type, value } = item ?? {}; if (type !== "String") { @@ -481,23 +511,19 @@ exports.parseString = (val) => { }; // Parse . -exports.parseColor = (val) => { +const parseColor = (val, opt = defaultOptions) => { const [item] = val; const { name, type, value } = item ?? {}; switch (type) { case "Function": { - const res = resolveColor(`${name}(${value})`, { - format: "specifiedValue" - }); + const res = resolveColor(`${name}(${value})`, opt); if (res) { return res; } break; } case "Hash": { - const res = resolveColor(`#${value}`, { - format: "specifiedValue" - }); + const res = resolveColor(`#${value}`, opt); if (res) { return res; } @@ -507,9 +533,7 @@ exports.parseColor = (val) => { if (SYS_COLOR.includes(name)) { return name; } - const res = resolveColor(name, { - format: "specifiedValue" - }); + const res = resolveColor(name, opt); if (res) { return res; } @@ -520,16 +544,35 @@ exports.parseColor = (val) => { }; // Parse . -exports.parseGradient = (val) => { +const parseGradient = (val, opt = defaultOptions) => { const [item] = val; const { name, type, value } = item ?? {}; if (type !== "Function") { return; } - const res = resolveGradient(`${name}(${value})`, { - format: "specifiedValue" - }); + const res = resolveGradient(`${name}(${value})`, opt); if (res) { return res; } }; + +module.exports = { + hasCalcFunc, + hasVarFunc, + isGlobalKeyword, + isValidPropertyValue, + parseAngle, + parseCSS, + parseColor, + parseGradient, + parseLength, + parseLengthPercentage, + parseNumber, + parsePercentage, + parsePropertyValue, + parseString, + parseURL, + prepareValue, + resolveCalc, + splitValue +}; diff --git a/lib/properties/background.js b/lib/properties/background.js index c94327bf..68251906 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -11,7 +11,8 @@ const backgroundAttachment = require("./backgroundAttachment"); const backgroundColor = require("./backgroundColor"); const property = "background"; -const initialValues = new Map([ + +module.exports.initialValues = new Map([ ["background-image", "none"], ["background-position", "0% 0%"], ["background-size", "auto"], @@ -34,13 +35,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; + const { globalObject, options } = opt; if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { - v = parsers.resolveCalc(v); + v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v)) { + if (!parsers.isValidPropertyValue(property, v, globalObject)) { return; } const values = parsers.splitValue(v, { @@ -50,24 +51,24 @@ module.exports.parse = function parse(v, opt = {}) { const l = values.length; for (let i = 0; i < l; i++) { let bg = { - "background-image": initialValues.get("background-image"), - "background-position": initialValues.get("background-position"), - "background-size": initialValues.get("background-size"), - "background-repeat": initialValues.get("background-repeat"), - "background-origin": initialValues.get("background-origin"), - "background-clip": initialValues.get("background-clip"), - "background-attachment": initialValues.get("background-attachment"), - "background-color": initialValues.get("background-color") + "background-image": module.exports.initialValues.get("background-image"), + "background-position": module.exports.initialValues.get("background-position"), + "background-size": module.exports.initialValues.get("background-size"), + "background-repeat": module.exports.initialValues.get("background-repeat"), + "background-origin": module.exports.initialValues.get("background-origin"), + "background-clip": module.exports.initialValues.get("background-clip"), + "background-attachment": module.exports.initialValues.get("background-attachment"), + "background-color": module.exports.initialValues.get("background-color") }; if (l > 1 && i !== l - 1) { bg = { - "background-image": initialValues.get("background-image"), - "background-position": initialValues.get("background-position"), - "background-size": initialValues.get("background-size"), - "background-repeat": initialValues.get("background-repeat"), - "background-origin": initialValues.get("background-origin"), - "background-clip": initialValues.get("background-clip"), - "background-attachment": initialValues.get("background-attachment") + "background-image": module.exports.initialValues.get("background-image"), + "background-position": module.exports.initialValues.get("background-position"), + "background-size": module.exports.initialValues.get("background-size"), + "background-repeat": module.exports.initialValues.get("background-repeat"), + "background-origin": module.exports.initialValues.get("background-origin"), + "background-clip": module.exports.initialValues.get("background-clip"), + "background-attachment": module.exports.initialValues.get("background-attachment") }; } const bgPosition = []; @@ -85,12 +86,12 @@ module.exports.parse = function parse(v, opt = {}) { for (const part of parts1) { let partValid = false; for (const [longhand, value] of module.exports.shorthandFor) { - if (parsers.isValidPropertyValue(longhand, part)) { + if (parsers.isValidPropertyValue(longhand, part, globalObject)) { partValid = true; switch (longhand) { case "background-clip": case "background-origin": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgBox.push(parsedValue); } @@ -100,31 +101,25 @@ module.exports.parse = function parse(v, opt = {}) { if (i !== values.length - 1) { return; } - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } break; } case "background-position": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgPosition.push(parsedValue); - } + bgPosition.push(part); break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgRepeat.push(parsedValue); - } + bgRepeat.push(part); break; } case "background-size": { break; } default: { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -141,12 +136,12 @@ module.exports.parse = function parse(v, opt = {}) { for (const part of parts2) { let partValid = false; for (const [longhand, value] of module.exports.shorthandFor) { - if (parsers.isValidPropertyValue(longhand, part)) { + if (parsers.isValidPropertyValue(longhand, part, globalObject)) { partValid = true; switch (longhand) { case "background-clip": case "background-origin": { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgBox.push(parsedValue); } @@ -156,7 +151,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 +161,15 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "background-repeat": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgRepeat.push(parsedValue); - } + bgRepeat.push(part); break; } case "background-size": { - const parsedValue = value.parse(part, { globalObject }); - if (parsedValue) { - bgSize.push(parsedValue); - } + bgSize.push(part); break; } default: { - const parsedValue = value.parse(part, { globalObject }); + const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bg[longhand] = parsedValue; } @@ -195,21 +184,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 +237,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; @@ -271,7 +261,7 @@ module.exports.definition = { const arr = bgMap.get(longhand); arr.push(value); bgMap.set(longhand, arr); - if (value !== initialValues.get(longhand)) { + if (value !== module.exports.initialValues.get(longhand)) { if (longhand === "background-size") { bg.push(`/ ${value}`); } else { @@ -314,7 +304,7 @@ module.exports.definition = { ) { return val; } - if (val !== initialValues.get(longhand)) { + if (val !== module.exports.initialValues.get(longhand)) { const imgValues = parsers.splitValue(val, { delimiter: "," }); @@ -322,10 +312,10 @@ module.exports.definition = { bgMap.set(longhand, imgValues); } } else if (longhand === "background-color") { - if (val !== initialValues.get(longhand) || v.includes(val)) { + if (val !== module.exports.initialValues.get(longhand) || v.includes(val)) { bgMap.set(longhand, [val]); } - } else if (val !== initialValues.get(longhand)) { + } else if (val !== module.exports.initialValues.get(longhand)) { bgMap.set( longhand, parsers.splitValue(val, { @@ -354,7 +344,7 @@ module.exports.definition = { if (parsers.hasVarFunc(value)) { return ""; } - if (value && value !== initialValues.get(longhand)) { + if (value && value !== module.exports.initialValues.get(longhand)) { const bgValue = bgValues[i]; bgValue.push(value); } @@ -366,7 +356,7 @@ module.exports.definition = { if (parsers.hasVarFunc(value)) { return ""; } - if (value && value !== initialValues.get(longhand)) { + if (value && value !== module.exports.initialValues.get(longhand)) { const bgValue = bgValues[i]; bgValue.push(`/ ${value}`); } @@ -377,7 +367,7 @@ module.exports.definition = { if (parsers.hasVarFunc(value)) { return ""; } - if (value && value !== initialValues.get(longhand)) { + if (value && value !== module.exports.initialValues.get(longhand)) { const bgValue = bgValues[i]; bgValue.push(value); } 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 c39f28c3..c3544b81 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(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -92,19 +97,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-width", name)) { + if (parsers.isValidPropertyValue("border-width", name, globalObject)) { if (parsedValues.has("border-width")) { return; } parsedValues.set("border-width", name); break; - } else if (parsers.isValidPropertyValue("border-style", name)) { + } else if (parsers.isValidPropertyValue("border-style", name, globalObject)) { if (parsedValues.has("border-style")) { return; } parsedValues.set("border-style", name); break; - } else if (parsers.isValidPropertyValue("border-color", name)) { + } else if (parsers.isValidPropertyValue("border-color", name, globalObject)) { if (parsedValues.has("border-color")) { return; } @@ -148,7 +153,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/borderBottom.js b/lib/properties/borderBottom.js index d624988b..381d85a7 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(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-bottom-width", name)) { + if (parsers.isValidPropertyValue("border-bottom-width", name, globalObject)) { if (parsedValues.has("border-bottom-width")) { return; } parsedValues.set("border-bottom-width", name); break; - } else if (parsers.isValidPropertyValue("border-bottom-style", name)) { + } else if (parsers.isValidPropertyValue("border-bottom-style", name, globalObject)) { if (parsedValues.has("border-bottom-style")) { return; } parsedValues.set("border-bottom-style", name); break; - } else if (parsers.isValidPropertyValue("border-bottom-color", name)) { + } else if (parsers.isValidPropertyValue("border-bottom-color", name, globalObject)) { if (parsedValues.has("border-bottom-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderBottomColor.js b/lib/properties/borderBottomColor.js index 015a60fd..fc54da4c 100644 --- a/lib/properties/borderBottomColor.js +++ b/lib/properties/borderBottomColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-bottom"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderBottomStyle.js b/lib/properties/borderBottomStyle.js index fc865b66..ad596756 100644 --- a/lib/properties/borderBottomStyle.js +++ b/lib/properties/borderBottomStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-bottom"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderBottomWidth.js b/lib/properties/borderBottomWidth.js index 5e6b682a..4305290f 100644 --- a/lib/properties/borderBottomWidth.js +++ b/lib/properties/borderBottomWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-bottom"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderCollapse.js b/lib/properties/borderCollapse.js index 55e56866..c8f49856 100644 --- a/lib/properties/borderCollapse.js +++ b/lib/properties/borderCollapse.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "border-collapse"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -34,7 +35,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/borderColor.js b/lib/properties/borderColor.js index 156bdd9e..6cb17df4 100644 --- a/lib/properties/borderColor.js +++ b/lib/properties/borderColor.js @@ -17,12 +17,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -40,7 +41,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - const parsedValue = parsers.parseColor([value]); + const parsedValue = parsers.parseColor([value], options); if (!parsedValue) { return; } @@ -98,7 +99,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeft.js b/lib/properties/borderLeft.js index aaf0e28e..ec0a8084 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(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-left-width", name)) { + if (parsers.isValidPropertyValue("border-left-width", name, globalObject)) { if (parsedValues.has("border-left-width")) { return; } parsedValues.set("border-left-width", name); break; - } else if (parsers.isValidPropertyValue("border-left-style", name)) { + } else if (parsers.isValidPropertyValue("border-left-style", name, globalObject)) { if (parsedValues.has("border-left-style")) { return; } parsedValues.set("border-left-style", name); break; - } else if (parsers.isValidPropertyValue("border-left-color", name)) { + } else if (parsers.isValidPropertyValue("border-left-color", name, globalObject)) { if (parsedValues.has("border-left-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeftColor.js b/lib/properties/borderLeftColor.js index d9c6b498..1f179628 100644 --- a/lib/properties/borderLeftColor.js +++ b/lib/properties/borderLeftColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-left"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeftStyle.js b/lib/properties/borderLeftStyle.js index 115106a1..d54928a9 100644 --- a/lib/properties/borderLeftStyle.js +++ b/lib/properties/borderLeftStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-left"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderLeftWidth.js b/lib/properties/borderLeftWidth.js index 51e20652..72e3bb83 100644 --- a/lib/properties/borderLeftWidth.js +++ b/lib/properties/borderLeftWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-left"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRight.js b/lib/properties/borderRight.js index cf1507e9..5a00f45f 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(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-right-width", name)) { + if (parsers.isValidPropertyValue("border-right-width", name, globalObject)) { if (parsedValues.has("border-right-width")) { return; } parsedValues.set("border-right-width", name); break; - } else if (parsers.isValidPropertyValue("border-right-style", name)) { + } else if (parsers.isValidPropertyValue("border-right-style", name, globalObject)) { if (parsedValues.has("border-right-style")) { return; } parsedValues.set("border-right-style", name); break; - } else if (parsers.isValidPropertyValue("border-right-color", name)) { + } else if (parsers.isValidPropertyValue("border-right-color", name, globalObject)) { if (parsedValues.has("border-right-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRightColor.js b/lib/properties/borderRightColor.js index 807b6df4..857ac14c 100644 --- a/lib/properties/borderRightColor.js +++ b/lib/properties/borderRightColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-right"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRightStyle.js b/lib/properties/borderRightStyle.js index 0d343c3b..ea43a6e3 100644 --- a/lib/properties/borderRightStyle.js +++ b/lib/properties/borderRightStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-right"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderRightWidth.js b/lib/properties/borderRightWidth.js index f4c5147d..beb95674 100644 --- a/lib/properties/borderRightWidth.js +++ b/lib/properties/borderRightWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-right"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderSpacing.js b/lib/properties/borderSpacing.js index d33df5a3..fee04259 100644 --- a/lib/properties/borderSpacing.js +++ b/lib/properties/borderSpacing.js @@ -5,19 +5,20 @@ const parsers = require("../parsers"); const property = "border-spacing"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { switch (value.length) { case 1: { const [part1] = value; - const val1 = parsers.parseLength([part1]); + const val1 = parsers.parseLength([part1], options); if (val1) { return val1; } @@ -25,8 +26,8 @@ module.exports.parse = function parse(v, opt = {}) { } case 2: { const [part1, part2] = value; - const val1 = parsers.parseLength([part1]); - const val2 = parsers.parseLength([part2]); + const val1 = parsers.parseLength([part1], options); + const val2 = parsers.parseLength([part2], options); if (val1 && val2) { return `${val1} ${val2}`; } @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/borderStyle.js b/lib/properties/borderStyle.js index ebeddbeb..c9c9f032 100644 --- a/lib/properties/borderStyle.js +++ b/lib/properties/borderStyle.js @@ -17,12 +17,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -98,7 +99,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTop.js b/lib/properties/borderTop.js index 80dec4f0..f07d19bd 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(value); + const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } @@ -82,19 +87,19 @@ module.exports.parse = function parse(v, opt = {}) { break; } case "Identifier": { - if (parsers.isValidPropertyValue("border-top-width", name)) { + if (parsers.isValidPropertyValue("border-top-width", name, globalObject)) { if (parsedValues.has("border-top-width")) { return; } parsedValues.set("border-top-width", name); break; - } else if (parsers.isValidPropertyValue("border-top-style", name)) { + } else if (parsers.isValidPropertyValue("border-top-style", name, globalObject)) { if (parsedValues.has("border-top-style")) { return; } parsedValues.set("border-top-style", name); break; - } else if (parsers.isValidPropertyValue("border-top-color", name)) { + } else if (parsers.isValidPropertyValue("border-top-color", name, globalObject)) { if (parsedValues.has("border-top-color")) { return; } @@ -138,7 +143,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (val || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTopColor.js b/lib/properties/borderTopColor.js index 8acfa672..be76ca59 100644 --- a/lib/properties/borderTopColor.js +++ b/lib/properties/borderTopColor.js @@ -8,12 +8,13 @@ const positionShorthand = "border-top"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -23,7 +24,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -38,7 +39,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTopStyle.js b/lib/properties/borderTopStyle.js index a90c2f68..0134de2c 100644 --- a/lib/properties/borderTopStyle.js +++ b/lib/properties/borderTopStyle.js @@ -8,12 +8,13 @@ const positionShorthand = "border-top"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -37,7 +38,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderTopWidth.js b/lib/properties/borderTopWidth.js index f97d124c..74eeaf5d 100644 --- a/lib/properties/borderTopWidth.js +++ b/lib/properties/borderTopWidth.js @@ -8,12 +8,13 @@ const positionShorthand = "border-top"; const shorthand = "border"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,9 +31,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLength(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLength(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/borderWidth.js b/lib/properties/borderWidth.js index b34496fa..92f27dd4 100644 --- a/lib/properties/borderWidth.js +++ b/lib/properties/borderWidth.js @@ -17,12 +17,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -51,9 +52,13 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLength([value], { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + const parsedValue = parsers.parseLength([value], parseOpt); if (!parsedValue) { return; } @@ -111,7 +116,8 @@ module.exports.definition = { this._borderSetter(property, v, ""); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/bottom.js b/lib/properties/bottom.js index 0e1902d1..dc1fd22d 100644 --- a/lib/properties/bottom.js +++ b/lib/properties/bottom.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "bottom"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/clear.js b/lib/properties/clear.js index cfd39c36..aa7eb085 100644 --- a/lib/properties/clear.js +++ b/lib/properties/clear.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "clear"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -34,7 +35,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/clip.js b/lib/properties/clip.js index 09908c0a..afcb59f2 100644 --- a/lib/properties/clip.js +++ b/lib/properties/clip.js @@ -7,12 +7,13 @@ const parsers = require("../parsers"); const property = "clip"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -24,8 +25,15 @@ module.exports.parse = function parse(v, opt = {}) { }); const parsedValues = []; for (const item of values) { - const parsedValue = parsers.parseCSS(item, { context: "value" }, true); - const val = parsers.parseLengthPercentage(parsedValue.children); + const parsedValue = parsers.parseCSS( + item, + { + globalObject, + options: { context: "value" } + }, + true + ); + const val = parsers.parseLengthPercentage(parsedValue.children, options); if (val) { parsedValues.push(val); } else { @@ -52,7 +60,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/color.js b/lib/properties/color.js index 74e398d0..9b48b9f9 100644 --- a/lib/properties/color.js +++ b/lib/properties/color.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/display.js b/lib/properties/display.js index fb45c479..de06987f 100644 --- a/lib/properties/display.js +++ b/lib/properties/display.js @@ -9,12 +9,13 @@ const displayOutside = ["block", "inline", "run-in"]; const displayFlow = ["flow", "flow-root"]; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -191,7 +192,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/flex.js b/lib/properties/flex.js index a319060e..dd484455 100644 --- a/lib/properties/flex.js +++ b/lib/properties/flex.js @@ -20,12 +20,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -115,6 +116,10 @@ module.exports.parse = function parse(v, opt = {}) { flex["flex-basis"] = `${val2.value}${val2.unit}`; break; } + case "Identifier": { + flex["flex-basis"] = val2.name; + break; + } case "Number": { flex["flex-shrink"] = val2.value; break; @@ -123,17 +128,12 @@ module.exports.parse = function parse(v, opt = {}) { flex["flex-basis"] = `${val2.value}%`; break; } - case "Identifier": { - flex["flex-basis"] = val2.name; - break; - } default: { return; } } } return flex; - // return [...Object.values(flex)].join(" "); } } else if (typeof value === "string") { return value; @@ -150,7 +150,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); const priority = this._priorities.get(property) ?? ""; if (typeof val === "string") { diff --git a/lib/properties/flexBasis.js b/lib/properties/flexBasis.js index 194ba579..b0bbd2dd 100644 --- a/lib/properties/flexBasis.js +++ b/lib/properties/flexBasis.js @@ -6,12 +6,13 @@ const property = "flex-basis"; const shorthand = "flex"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -28,7 +29,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -44,7 +45,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/flexGrow.js b/lib/properties/flexGrow.js index 89537d09..e40d7b89 100644 --- a/lib/properties/flexGrow.js +++ b/lib/properties/flexGrow.js @@ -6,12 +6,13 @@ const property = "flex-grow"; const shorthand = "flex"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue("flex-grow", v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseNumber(value); + return parsers.parseNumber(value, options); } } } else if (typeof value === "string") { @@ -43,7 +44,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/flexShrink.js b/lib/properties/flexShrink.js index 7b205a4b..c4927189 100644 --- a/lib/properties/flexShrink.js +++ b/lib/properties/flexShrink.js @@ -6,12 +6,13 @@ const property = "flex-shrink"; const shorthand = "flex"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseNumber(value); + return parsers.parseNumber(value, options); } } } else if (typeof value === "string") { @@ -43,7 +44,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/float.js b/lib/properties/float.js index 3e256318..6ffb6215 100644 --- a/lib/properties/float.js +++ b/lib/properties/float.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "float"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -34,7 +35,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/floodColor.js b/lib/properties/floodColor.js index 280bb112..7d1ded01 100644 --- a/lib/properties/floodColor.js +++ b/lib/properties/floodColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "flood-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/font.js b/lib/properties/font.js index 4cbcf553..804832f1 100644 --- a/lib/properties/font.js +++ b/lib/properties/font.js @@ -20,13 +20,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; + const { globalObject, options } = opt; if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { - v = parsers.resolveCalc(v); + v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v)) { + if (!parsers.isValidPropertyValue(property, v, globalObject)) { return; } const [fontBlock, ...families] = parsers.splitValue(v, { @@ -47,13 +47,15 @@ module.exports.parse = function parse(v, opt = {}) { return; } const lineHeightB = lineHeight.parse(lineB, { - global + globalObject, + options }); if (typeof lineHeightB !== "string") { return; } const familyB = fontFamily.parse(familiesB.join(" "), { globalObject, + options, caseSensitive: true }); if (typeof familyB === "string") { @@ -71,7 +73,8 @@ module.exports.parse = function parse(v, opt = {}) { switch (longhand) { case "font-size": { const parsedValue = fontSize.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { font[longhand] = parsedValue; @@ -83,7 +86,8 @@ module.exports.parse = function parse(v, opt = {}) { if (font[longhand] === "normal") { const longhandItem = module.exports.shorthandFor.get(longhand); const parsedValue = longhandItem.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { font[longhand] = parsedValue; @@ -94,7 +98,8 @@ module.exports.parse = function parse(v, opt = {}) { case "font-variant": { if (font[longhand] === "normal") { const parsedValue = fontVariant.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { if (parsedValue === "small-cap") { @@ -122,6 +127,7 @@ module.exports.parse = function parse(v, opt = {}) { const [part] = revParts; const value = parsers.parsePropertyValue(property, part, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -158,7 +164,8 @@ module.exports.parse = function parse(v, opt = {}) { if (font[longhand] === "normal") { const longhandItem = module.exports.shorthandFor.get(longhand); const parsedValue = longhandItem.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { font[longhand] = parsedValue; @@ -169,7 +176,8 @@ module.exports.parse = function parse(v, opt = {}) { case "font-variant": { if (font[longhand] === "normal") { const parsedValue = fontVariant.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedValue === "string") { if (parsedValue === "small-cap") { @@ -187,13 +195,15 @@ module.exports.parse = function parse(v, opt = {}) { } } else { const parsedFontSize = fontSize.parse(part, { - globalObject + globalObject, + options }); if (typeof parsedFontSize === "string") { fontSizeA = parsedFontSize; } else { const parsedFontFamily = fontFamily.parse(part, { globalObject, + options, caseSensitive: true }); if (typeof parsedFontFamily === "string") { @@ -206,6 +216,7 @@ module.exports.parse = function parse(v, opt = {}) { } const family = fontFamily.parse(revFontFamily.toReversed().join(" "), { globalObject, + options, caseSensitive: true }); if (fontSizeA && family) { @@ -218,6 +229,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const family of families) { const parsedFontFamily = fontFamily.parse(family, { globalObject, + options, caseSensitive: true }); if (parsedFontFamily) { @@ -240,7 +252,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const obj = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (!obj) { return; diff --git a/lib/properties/fontFamily.js b/lib/properties/fontFamily.js index db57001d..402cefae 100644 --- a/lib/properties/fontFamily.js +++ b/lib/properties/fontFamily.js @@ -6,10 +6,10 @@ const property = "font-family"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); @@ -17,6 +17,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, caseSensitive: true, inArray: true }); @@ -76,7 +77,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontSize.js b/lib/properties/fontSize.js index cdffa8e7..bcef3e6a 100644 --- a/lib/properties/fontSize.js +++ b/lib/properties/fontSize.js @@ -6,12 +6,13 @@ const property = "font-size"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -28,9 +29,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -46,7 +51,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontStyle.js b/lib/properties/fontStyle.js index b296798d..640afe90 100644 --- a/lib/properties/fontStyle.js +++ b/lib/properties/fontStyle.js @@ -6,12 +6,13 @@ const property = "font-style"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { } else if (value.length === 2) { const [part1, part2] = value; const val1 = part1.type === "Identifier" && part1.name; - const val2 = parsers.parseAngle([part2]); + const val2 = parsers.parseAngle([part2], options); if (val1 && val1 === "oblique" && val2) { return `${val1} ${val2}`; } @@ -45,7 +46,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontVariant.js b/lib/properties/fontVariant.js index b0d7749d..4e230fa7 100644 --- a/lib/properties/fontVariant.js +++ b/lib/properties/fontVariant.js @@ -6,15 +6,16 @@ const property = "font-variant"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = []; for (const val of values) { const value = parsers.parsePropertyValue(property, val, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -55,7 +56,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/fontWeight.js b/lib/properties/fontWeight.js index d115a9dd..0bb826b4 100644 --- a/lib/properties/fontWeight.js +++ b/lib/properties/fontWeight.js @@ -6,12 +6,13 @@ const property = "font-weight"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -28,10 +29,14 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - const parsedValue = parsers.parseNumber(value, { + const parseOpt = { min: 1, max: 1000 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + const parsedValue = parsers.parseNumber(value, parseOpt); if (parsedValue) { return parsedValue; } @@ -50,7 +55,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/height.js b/lib/properties/height.js index 977d05f7..100e5ba6 100644 --- a/lib/properties/height.js +++ b/lib/properties/height.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "height"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/left.js b/lib/properties/left.js index 8b8b8d01..5217e26c 100644 --- a/lib/properties/left.js +++ b/lib/properties/left.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "left"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/lightingColor.js b/lib/properties/lightingColor.js index e1e321d4..21773f5b 100644 --- a/lib/properties/lightingColor.js +++ b/lib/properties/lightingColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "lighting-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/lineHeight.js b/lib/properties/lineHeight.js index f7eb7598..ee60c563 100644 --- a/lib/properties/lineHeight.js +++ b/lib/properties/lineHeight.js @@ -6,16 +6,23 @@ const property = "line-height"; const shorthand = "font"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { const [{ name, type, value: itemValue }] = value; + const parseOpt = { + min: 0 + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } switch (type) { case "Calc": { return `${name}(${itemValue})`; @@ -25,14 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { return name; } case "Number": { - return parsers.parseNumber(value, { - min: 0 - }); + return parsers.parseNumber(value, parseOpt); } default: { - return parsers.parseLengthPercentage(value, { - min: 0 - }); + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -48,7 +51,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/margin.js b/lib/properties/margin.js index df68cc04..ca3c6f9b 100644 --- a/lib/properties/margin.js +++ b/lib/properties/margin.js @@ -18,12 +18,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -53,7 +54,7 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLengthPercentage([value]); + const parsedValue = parsers.parseLengthPercentage([value], options); if (!parsedValue) { return; } @@ -79,7 +80,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/marginBottom.js b/lib/properties/marginBottom.js index 26aa15c0..f4cd4ddf 100644 --- a/lib/properties/marginBottom.js +++ b/lib/properties/marginBottom.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "bottom"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/marginLeft.js b/lib/properties/marginLeft.js index ae7ad63f..c38ed941 100644 --- a/lib/properties/marginLeft.js +++ b/lib/properties/marginLeft.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "left"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/marginRight.js b/lib/properties/marginRight.js index 41aca507..2e4a2174 100644 --- a/lib/properties/marginRight.js +++ b/lib/properties/marginRight.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "right"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/marginTop.js b/lib/properties/marginTop.js index e33edf71..b16a9bee 100644 --- a/lib/properties/marginTop.js +++ b/lib/properties/marginTop.js @@ -8,12 +8,13 @@ const shorthand = "margin"; module.exports.position = "top"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -30,7 +31,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -46,7 +47,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/opacity.js b/lib/properties/opacity.js index 467a349c..c252c05a 100644 --- a/lib/properties/opacity.js +++ b/lib/properties/opacity.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "opacity"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -24,10 +25,10 @@ module.exports.parse = function parse(v, opt = {}) { return name; } case "Number": { - return parsers.parseNumber(value); + return parsers.parseNumber(value, options); } case "Percentage": { - return parsers.parsePercentage(value); + return parsers.parsePercentage(value, options); } default: } @@ -43,7 +44,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/outlineColor.js b/lib/properties/outlineColor.js index be62e661..a6ed9680 100644 --- a/lib/properties/outlineColor.js +++ b/lib/properties/outlineColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "outline-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/padding.js b/lib/properties/padding.js index b574762f..c3d3f3ee 100644 --- a/lib/properties/padding.js +++ b/lib/properties/padding.js @@ -18,12 +18,13 @@ module.exports.shorthandFor = new Map([ ]); module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const values = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); const parsedValues = []; @@ -49,9 +50,13 @@ module.exports.parse = function parse(v, opt = {}) { break; } default: { - const parsedValue = parsers.parseLengthPercentage([value], { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + const parsedValue = parsers.parseLengthPercentage([value], parseOpt); if (!parsedValue) { return; } @@ -77,7 +82,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (Array.isArray(val) || typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/paddingBottom.js b/lib/properties/paddingBottom.js index a2ec94da..89a6058e 100644 --- a/lib/properties/paddingBottom.js +++ b/lib/properties/paddingBottom.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "bottom"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/paddingLeft.js b/lib/properties/paddingLeft.js index fc67fd41..273fac3b 100644 --- a/lib/properties/paddingLeft.js +++ b/lib/properties/paddingLeft.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "left"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/paddingRight.js b/lib/properties/paddingRight.js index d6eb1ec7..f883f021 100644 --- a/lib/properties/paddingRight.js +++ b/lib/properties/paddingRight.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "right"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/paddingTop.js b/lib/properties/paddingTop.js index 59c84b86..7f44bf40 100644 --- a/lib/properties/paddingTop.js +++ b/lib/properties/paddingTop.js @@ -8,12 +8,13 @@ const shorthand = "padding"; module.exports.position = "top"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -29,9 +30,13 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value, { + const parseOpt = { min: 0 - }); + }; + for (const [key, val] of Object.entries(options)) { + parseOpt[key] = val; + } + return parsers.parseLengthPercentage(value, parseOpt); } } } else if (typeof value === "string") { @@ -47,7 +52,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const shorthandPriority = this._priorities.get(shorthand); diff --git a/lib/properties/right.js b/lib/properties/right.js index 46906797..38ece0fd 100644 --- a/lib/properties/right.js +++ b/lib/properties/right.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "right"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/stopColor.js b/lib/properties/stopColor.js index 65c605c4..b3fa2114 100644 --- a/lib/properties/stopColor.js +++ b/lib/properties/stopColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "stop-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/top.js b/lib/properties/top.js index 6da042ed..e8af5342 100644 --- a/lib/properties/top.js +++ b/lib/properties/top.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "top"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderAfterColor.js b/lib/properties/webkitBorderAfterColor.js index d9d7411d..ee0bc4fa 100644 --- a/lib/properties/webkitBorderAfterColor.js +++ b/lib/properties/webkitBorderAfterColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-after-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderBeforeColor.js b/lib/properties/webkitBorderBeforeColor.js index 6d61e5a7..6dc7f996 100644 --- a/lib/properties/webkitBorderBeforeColor.js +++ b/lib/properties/webkitBorderBeforeColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-before-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderEndColor.js b/lib/properties/webkitBorderEndColor.js index b8c6debf..fb58c297 100644 --- a/lib/properties/webkitBorderEndColor.js +++ b/lib/properties/webkitBorderEndColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-end-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitBorderStartColor.js b/lib/properties/webkitBorderStartColor.js index e60d1df4..e75c4a72 100644 --- a/lib/properties/webkitBorderStartColor.js +++ b/lib/properties/webkitBorderStartColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-border-start-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitColumnRuleColor.js b/lib/properties/webkitColumnRuleColor.js index 0db57961..ec7e55da 100644 --- a/lib/properties/webkitColumnRuleColor.js +++ b/lib/properties/webkitColumnRuleColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-column-rule-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTapHighlightColor.js b/lib/properties/webkitTapHighlightColor.js index 06596563..f5f97d0a 100644 --- a/lib/properties/webkitTapHighlightColor.js +++ b/lib/properties/webkitTapHighlightColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-tap-highlight-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTextEmphasisColor.js b/lib/properties/webkitTextEmphasisColor.js index 84dac5ec..ab31c6ef 100644 --- a/lib/properties/webkitTextEmphasisColor.js +++ b/lib/properties/webkitTextEmphasisColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-text-emphasis-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTextFillColor.js b/lib/properties/webkitTextFillColor.js index f13dc649..dbb1aacd 100644 --- a/lib/properties/webkitTextFillColor.js +++ b/lib/properties/webkitTextFillColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-text-fill-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/webkitTextStrokeColor.js b/lib/properties/webkitTextStrokeColor.js index 2339702d..93241044 100644 --- a/lib/properties/webkitTextStrokeColor.js +++ b/lib/properties/webkitTextStrokeColor.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "-webkit-text-stroke-color"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -20,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseColor(value); + return parsers.parseColor(value, options); } } } else if (typeof value === "string") { @@ -35,7 +36,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/properties/width.js b/lib/properties/width.js index 6c60911f..6b212563 100644 --- a/lib/properties/width.js +++ b/lib/properties/width.js @@ -5,12 +5,13 @@ const parsers = require("../parsers"); const property = "width"; module.exports.parse = function parse(v, opt = {}) { - const { globalObject } = opt; if (v === "") { return v; } + const { globalObject, options } = opt; const value = parsers.parsePropertyValue(property, v, { globalObject, + options, inArray: true }); if (Array.isArray(value) && value.length === 1) { @@ -27,7 +28,7 @@ module.exports.parse = function parse(v, opt = {}) { return name; } default: { - return parsers.parseLengthPercentage(value); + return parsers.parseLengthPercentage(value, options); } } } else if (typeof value === "string") { @@ -42,7 +43,8 @@ module.exports.definition = { this._setProperty(property, v); } else { const val = module.exports.parse(v, { - globalObject: this._global + globalObject: this._global, + options: this._options }); if (typeof val === "string") { const priority = this._priorities.get(property) ?? ""; diff --git a/lib/utils/cache.js b/lib/utils/cache.js new file mode 100644 index 00000000..2669eb24 --- /dev/null +++ b/lib/utils/cache.js @@ -0,0 +1,34 @@ +"use strict"; + +const { LRUCache } = require("lru-cache"); + +// The lru-cache instance. +const lruCache = new LRUCache({ + max: 4096 +}); + +// The lru-cache requires non-null values, so substitute in this sentinel when we are given one. +const nullSentinel = Symbol("null"); + +/** + * @param key - Cache key. + * @param value - Value to cache. + * @returns void. + */ +const setCache = (key, value) => { + lruCache.set(key, value === null ? nullSentinel : value); +}; + +/** + * @param key - Cache key. + * @returns Cached item or false otherwise. + */ +const getCache = (key) => { + const value = lruCache.get(key); + return value === nullSentinel ? null : value; +}; + +module.exports = { + getCache, + setCache +}; diff --git a/lib/utils/propertyDescriptors.js b/lib/utils/propertyDescriptors.js index b0053db8..12612586 100644 --- a/lib/utils/propertyDescriptors.js +++ b/lib/utils/propertyDescriptors.js @@ -5,10 +5,66 @@ const parsers = require("../parsers"); exports.getPropertyDescriptor = function getPropertyDescriptor(property) { return { set(v) { - v = parsers.parsePropertyValue(property, v, { - globalObject: this._global - }); - this._setProperty(property, v); + const globalObject = this._global; + v = parsers.prepareValue(v, globalObject); + if (v === "" || parsers.hasVarFunc(v)) { + this._setProperty(property, v); + } else { + const options = this._options; + const val = parsers.parsePropertyValue(property, v, { + globalObject, + options, + inArray: true + }); + let value; + if (Array.isArray(val) && val.length === 1) { + const [{ name, raw, type, value: itemValue }] = val; + switch (type) { + case "Angle": { + value = parsers.parseAngle(val, options); + break; + } + case "Calc": { + value = `${name}(${itemValue})`; + break; + } + case "Dimension": { + value = parsers.parseLength(val, options); + break; + } + case "GlobalKeyword": + case "Identifier": { + value = name; + break; + } + case "Number": { + value = parsers.parseNumber(val, options); + break; + } + case "Percentage": { + value = parsers.parsePercentage(val, options); + break; + } + case "String": { + value = parsers.parseString(val, options); + break; + } + case "Url": { + value = parsers.parseURL(val, options); + break; + } + default: { + value = raw; + } + } + } else if (typeof val === "string") { + value = val; + } + if (typeof value === "string") { + const priority = this._priorities.get(property) ?? ""; + this._setProperty(property, value, priority); + } + } }, get() { return this.getPropertyValue(property); diff --git a/package-lock.json b/package-lock.json index a7a46446..d236837b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,40 +9,41 @@ "version": "5.3.3", "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^4.0.3", - "@csstools/css-syntax-patches-for-csstree": "^1.0.14", - "css-tree": "^3.1.0" + "@asamuzakjp/css-color": "^4.0.5", + "@csstools/css-syntax-patches-for-csstree": "^1.0.15", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.2" }, "devDependencies": { - "@babel/generator": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.2", + "@babel/generator": "^7.28.5", + "@babel/parser": "^7.28.5", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^6.23.6", - "eslint": "^9.32.0", + "@webref/css": "^8.0.0", + "eslint": "^9.39.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.5.0", + "npm-run-all2": "^8.0.4", "prettier": "^3.6.2", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "engines": { "node": ">=20" } }, "node_modules/@asamuzakjp/css-color": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.3.tgz", - "integrity": "sha512-aixYrB8ESx3o1DnhOQsvjg5OCX0eK2pu4IjEKLbPhJofpREoUZDRp9orC6uUDih0V86eX8FINeU65LwiLOk32g==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz", + "integrity": "sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==", "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.4", - "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", - "lru-cache": "^11.1.0" + "lru-cache": "^11.2.1" } }, "node_modules/@babel/code-frame": { @@ -61,14 +62,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -98,9 +99,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -108,13 +109,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -139,18 +140,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -158,23 +159,23 @@ } }, "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.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "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": { @@ -255,6 +256,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -263,9 +265,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.14.tgz", - "integrity": "sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.15.tgz", + "integrity": "sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==", "funding": [ { "type": "github", @@ -279,9 +281,6 @@ "license": "MIT-0", "engines": { "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.4" } }, "node_modules/@csstools/css-tokenizer": { @@ -299,6 +298,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -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": { @@ -356,13 +356,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -371,19 +371,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -431,9 +434,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.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz", + "integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==", "dev": true, "license": "MIT", "engines": { @@ -444,9 +447,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -454,13 +457,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -600,9 +603,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": "8.0.0", + "resolved": "https://registry.npmjs.org/@webref/css/-/css-8.0.0.tgz", + "integrity": "sha512-1uQbu0veKzLNyT5xUmbNj9mjpXi/hcMSBmutrUEcX45sDQlcc3/f7CUPPxu0nKZbVJy2fB5/ZlZMNiH3rwPlyQ==", "dev": true, "license": "MIT", "peerDependencies": { @@ -615,6 +618,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -672,71 +676,6 @@ "dev": true, "license": "Python-2.0" }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -755,56 +694,6 @@ "concat-map": "0.0.1" } }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -879,6 +768,7 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "license": "MIT", + "peer": true, "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" @@ -887,60 +777,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 +802,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,25 +816,25 @@ } }, "node_modules/eslint": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", - "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz", + "integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==", "dev": true, "license": "MIT", + "peer": true, "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-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.32.0", - "@eslint/plugin-kit": "^0.3.4", + "@eslint/js": "9.39.0", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -1240,6 +882,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -1251,9 +894,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 +1097,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,168 +1107,30 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.13.0" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/has-flag": { @@ -1654,64 +1143,6 @@ "node": ">=8" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1725,13 +1156,6 @@ "node": ">= 0.4" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, - "license": "ISC" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1769,1469 +1193,490 @@ "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==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "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==", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "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==", + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "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" + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "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==", + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "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==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "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", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^18.17.0 || >=20.5.0" } }, - "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==", + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "json-buffer": "3.0.1" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "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==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "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", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "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", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "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", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "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==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "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", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "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==", - "license": "ISC", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "license": "CC0-1.0" - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "peer": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "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==", - "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" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "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" - }, - "bin": { - "npm-run-all": "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_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "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==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "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_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "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", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "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", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "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" - }, + "node_modules/lru-cache": { + "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": ">=4" + "node": "20 || >=22" } }, - "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/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "license": "CC0-1.0" }, - "node_modules/pidtree": { + "node_modules/memorystream": { "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==", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", "dev": true, - "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.10.0" } }, - "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/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "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, + "license": "ISC", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "brace-expansion": "^1.1.7" }, "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": "*" } }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } + "license": "MIT" }, - "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==", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } + "license": "MIT" }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "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": "MIT", + "license": "ISC", "engines": { - "node": ">=6" + "node": "^18.17.0 || >=20.5.0" } }, - "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==", + "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": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.6", + "memorystream": "^0.3.1", + "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": "^20.5.0 || >=22.0.0", + "npm": ">= 10" } }, - "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==", + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "dependencies": { - "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" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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==", + "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": { - "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" - }, + "license": "ISC", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=16" } }, - "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==", + "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": "MIT", + "license": "ISC", "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "isexe": "^3.1.1" }, "bin": { - "resolve": "bin/resolve" + "node-which": "bin/which.js" }, "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": "^18.17.0 || >=20.5.0" } }, - "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==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "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" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "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==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "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" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" + "callsites": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "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==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, "engines": { "node": ">=8" } }, - "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/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, - "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/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "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": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "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/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", - "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" + "bin": { + "pidtree": "bin/pidtree.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10" } }, - "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/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", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8.0" } }, - "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/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", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" + "peer": true, + "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-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/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": { - "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" - }, - "engines": { - "node": ">= 0.4" + "fast-diff": "^1.1.2" }, - "funding": { - "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": ">=6.0.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==", + "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": "Apache-2.0", - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "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==", + "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": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "json-parse-even-better-errors": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.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": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "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" + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" @@ -3240,58 +1685,45 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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/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.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" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "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-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-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" + } + }, + "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", + "engines": { + "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 +1731,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 +1808,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 +1818,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 +1834,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 e9150c88..2f2415f9 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "keywords": [ "CSS", "CSSStyleDeclaration", + "CSSStyleProperties", "StyleSheet" ], "version": "5.3.3", @@ -35,37 +36,39 @@ "files": [ "lib/" ], - "main": "./lib/CSSStyleDeclaration.js", + "main": "./lib/index.js", "dependencies": { - "@asamuzakjp/css-color": "^4.0.3", - "@csstools/css-syntax-patches-for-csstree": "^1.0.14", - "css-tree": "^3.1.0" + "@asamuzakjp/css-color": "^4.0.5", + "@csstools/css-syntax-patches-for-csstree": "^1.0.15", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.2" }, "devDependencies": { - "@babel/generator": "^7.28.0", - "@babel/parser": "^7.28.0", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.2", + "@babel/generator": "^7.28.5", + "@babel/parser": "^7.28.5", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^6.23.6", - "eslint": "^9.32.0", + "@webref/css": "^8.0.0", + "eslint": "^9.39.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.5.0", + "npm-run-all2": "^8.0.4", "prettier": "^3.6.2", - "resolve": "^1.22.10" + "resolve": "^1.22.11" }, "scripts": { - "download": "node ./scripts/downloadLatestProperties.mjs", + "download": "node ./scripts/downloadLatestProperties.mjs && npm run generate", "generate": "run-p generate:*", - "generate:implemented_properties": "node ./scripts/generateImplementedProperties.mjs", "generate:properties": "node ./scripts/generateProperties.js", + "generate:propertyList": "node ./scripts/generatePropertyList.mjs", "lint": "npm run generate && eslint --max-warnings 0", "lint:fix": "eslint --fix --max-warnings 0", - "prepublishOnly": "npm run lint && npm run test", - "test": "npm run generate && node --test", - "test-ci": "npm run lint && npm run test" + "prepublishOnly": "npm run test-ci", + "test": "npm run generate && npm run test:unit", + "test:unit": "node --test", + "test-ci": "npm run lint && npm run test:unit" }, "license": "MIT", "engines": { diff --git a/lib/utils/camelize.js b/scripts/camelize.js similarity index 94% rename from lib/utils/camelize.js rename to scripts/camelize.js index 19aaf7de..7f3fcefe 100644 --- a/lib/utils/camelize.js +++ b/scripts/camelize.js @@ -1,6 +1,6 @@ "use strict"; -const { asciiLowercase } = require("./strings"); +const { asciiLowercase } = require("../lib/utils/strings"); // Utility to translate from `border-width` to `borderWidth`. // NOTE: For values prefixed with webkit, e.g. `-webkit-foo`, we need to provide diff --git a/scripts/downloadLatestProperties.mjs b/scripts/downloadLatestProperties.mjs index 28fa2edf..ad3beae3 100644 --- a/scripts/downloadLatestProperties.mjs +++ b/scripts/downloadLatestProperties.mjs @@ -6,11 +6,8 @@ import fs from "node:fs"; import path from "node:path"; -import { fileURLToPath } from "node:url"; const url = "https://www.w3.org/Style/CSS/all-properties.en.json"; -const outputFile = resolve("../lib/generated/allProperties.js"); -const unwantedStatuses = new Set(["NOTE", "ED"]); console.log("Downloading CSS properties..."); @@ -22,17 +19,17 @@ if (res.status !== 200) { const rawCSSProperties = await res.json(); const propertyNames = new Set(); +const unwantedStatuses = new Set(["NOTE", "ED"]); for (const cssProp of rawCSSProperties) { // Filter out properties from too-new statuses. // Also, '--*' needs additional logic to this module, so filter it out for now. if (unwantedStatuses.has(cssProp.status) || cssProp.property === "--*") { continue; } - propertyNames.add(cssProp.property); } -const propertyNamesJSON = JSON.stringify(Array.from(propertyNames), undefined, 2); +const propertyNamesJSON = JSON.stringify([...propertyNames], undefined, 2); const dateToday = new Date(); const [dateTodayFormatted] = dateToday.toISOString().split("T"); const output = `"use strict"; @@ -42,9 +39,6 @@ const output = `"use strict"; module.exports = new Set(${propertyNamesJSON}); `; +const { dirname } = import.meta; +const outputFile = path.resolve(dirname, "../lib/generated/allProperties.js"); fs.writeFileSync(outputFile, output); - -// TODO: remove when we can drop Node.js 18 support and use import.meta.dirname. -function resolve(relativePath) { - return path.resolve(path.dirname(fileURLToPath(import.meta.url)), relativePath); -} diff --git a/scripts/generateImplementedProperties.mjs b/scripts/generateImplementedProperties.mjs deleted file mode 100644 index efa807a0..00000000 --- a/scripts/generateImplementedProperties.mjs +++ /dev/null @@ -1,42 +0,0 @@ -import fs from "node:fs"; -import path from "node:path"; -import css from "@webref/css"; -import { camelCaseToDashed } from "../lib/utils/camelize.js"; - -const parsedFiles = await css.listAll(); -const definitions = new Map(); -for (const { properties } of Object.values(parsedFiles)) { - if (Array.isArray(properties)) { - for (const definition of properties) { - const { name } = definition; - if (name) { - definitions.set(name, definition); - } - } - } -} - -const { dirname } = import.meta; -const dashedProperties = fs - .readdirSync(path.resolve(dirname, "../lib/properties")) - .filter((propertyFile) => path.extname(propertyFile) === ".js") - .map((propertyFile) => camelCaseToDashed(path.basename(propertyFile, ".js"))) - .toSorted(); - -const implementedProperties = new Map(); -for (const property of dashedProperties) { - const definition = definitions.get(property); - implementedProperties.set(property, definition); -} - -const outputFile = path.resolve(dirname, "../lib/generated/implementedProperties.js"); - -const dateToday = new Date(); -const [dateTodayFormatted] = dateToday.toISOString().split("T"); -const output = `"use strict"; -// autogenerated - ${dateTodayFormatted} - -module.exports = new Map(${JSON.stringify([...implementedProperties], null, 2)}); -`; - -fs.writeFileSync(outputFile, output); diff --git a/scripts/generateProperties.js b/scripts/generateProperties.js index 61aec085..30372f2d 100644 --- a/scripts/generateProperties.js +++ b/scripts/generateProperties.js @@ -7,7 +7,7 @@ const t = require("@babel/types"); const generate = require("@babel/generator").default; const traverse = require("@babel/traverse").default; const resolve = require("resolve"); -const { camelCaseToDashed } = require("../lib/utils/camelize"); +const { camelCaseToDashed } = require("./camelize"); const { basename, dirname } = nodePath; diff --git a/scripts/generatePropertyList.mjs b/scripts/generatePropertyList.mjs new file mode 100644 index 00000000..c5bfb02c --- /dev/null +++ b/scripts/generatePropertyList.mjs @@ -0,0 +1,47 @@ +import fs from "node:fs"; +import path from "node:path"; +import css from "@webref/css"; +import allProperties from "../lib/generated/allProperties.js"; +import allExtraProperties from "../lib/utils/allExtraProperties.js"; + +const unifiedProperties = + typeof allProperties.union === "function" + ? allProperties.union(allExtraProperties) + : new Set([...allProperties, ...allExtraProperties]); + +const { properties } = await css.listAll(); +const definitions = new Map(); +if (Array.isArray(properties)) { + for (const definition of properties) { + const { href, initial, inherited, computedValue, legacyAliasOf, name, styleDeclaration } = + definition; + if (unifiedProperties.has(name)) { + definitions.set(name, { + href, + initial, + inherited, + computedValue, + legacyAliasOf, + name, + styleDeclaration + }); + } + } +} + +const propertyList = new Map(); +for (const property of [...unifiedProperties].toSorted()) { + const definition = definitions.get(property); + if (definition) { + propertyList.set(property, definition); + } +} +const [dateTodayFormatted] = new Date().toISOString().split("T"); +const output = `"use strict"; +// autogenerated - ${dateTodayFormatted} + +module.exports = new Map(${JSON.stringify([...propertyList], null, 2)}); +`; +const { dirname } = import.meta; +const outputFile = path.resolve(dirname, "../lib/generated/propertyList.js"); +fs.writeFileSync(outputFile, output); diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 91ab46e3..d0c82680 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -2,50 +2,17 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); -const { CSSStyleDeclaration, propertyList } = require("../lib/CSSStyleDeclaration"); -const allProperties = require("../lib/generated/allProperties"); -const implementedProperties = require("../lib/generated/implementedProperties"); -const allExtraProperties = require("../lib/utils/allExtraProperties"); -const camelize = require("../lib/utils/camelize"); +const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); describe("CSSStyleDeclaration", () => { - const dashedProperties = [...allProperties, ...allExtraProperties]; - const allowedProperties = dashedProperties.map(camelize.dashedToCamelCase); - const invalidProperties = [...implementedProperties.keys()] - .map(camelize.dashedToCamelCase) - .filter((prop) => !allowedProperties.includes(prop)); - - it("has only valid properties implemented", () => { - assert.strictEqual(invalidProperties.length, 0); - }); - - it("does not enumerate constructor or internals", () => { - const style = new CSSStyleDeclaration(); - assert.strictEqual(Object.getOwnPropertyDescriptor(style, "constructor").enumerable, false); - for (const i in style) { - assert.strictEqual(i.startsWith("_"), false); - } - }); - - it("has all properties", () => { - const style = new CSSStyleDeclaration(); - allProperties.forEach((property) => { - assert.ok(style.__lookupGetter__(property)); - assert.ok(style.__lookupSetter__(property)); - }); - }); - - it("has dashed properties", () => { - const style = new CSSStyleDeclaration(); - dashedProperties.forEach((property) => { - assert.ok(style.__lookupGetter__(property)); - assert.ok(style.__lookupSetter__(property)); - }); - }); - - it("has all functions", () => { - const style = new CSSStyleDeclaration(); - + const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError + }; + + it("has methods", () => { + const style = new CSSStyleDeclaration(window); assert.strictEqual(typeof style.item, "function"); assert.strictEqual(typeof style.getPropertyValue, "function"); assert.strictEqual(typeof style.setProperty, "function"); @@ -53,34 +20,8 @@ describe("CSSStyleDeclaration", () => { assert.strictEqual(typeof style.removeProperty, "function"); }); - it("has PascalCase for webkit prefixed properties", () => { - const style = new CSSStyleDeclaration(); - for (const i in style) { - if (/^webkit[A-Z]/.test(i)) { - const pascal = i.replace(/^webkit/, "Webkit"); - assert.ok(style[pascal] !== undefined); - } - } - }); - - it("throws if argument is not given", () => { - const style = new CSSStyleDeclaration(); - - assert.throws( - () => { - style.item(); - }, - (e) => { - assert.strictEqual(e instanceof globalThis.TypeError, true); - assert.strictEqual(e.message, "1 argument required, but only 0 present."); - return true; - } - ); - }); - - it("has special properties", () => { - const style = new CSSStyleDeclaration(); - + it("has attributes", () => { + const style = new CSSStyleDeclaration(window); assert.ok(style.__lookupGetter__("cssText")); assert.ok(style.__lookupSetter__("cssText")); assert.ok(style.__lookupGetter__("length")); @@ -88,141 +29,24 @@ describe("CSSStyleDeclaration", () => { assert.ok(style.__lookupGetter__("parentRule")); }); - it("sets internals for Window", () => { - const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException - }; - const style = new CSSStyleDeclaration(null, { - context: window - }); - - assert.strictEqual(style.cssText, ""); - assert.throws( - () => { - style.cssText = "color: green;"; - }, - (e) => { - assert.strictEqual(e instanceof window.DOMException, true); - assert.strictEqual(e.name, "NoModificationAllowedError"); - assert.strictEqual(e.message, "cssText can not be modified."); - return true; - } - ); - assert.throws( - () => { - style.removeProperty("color"); - }, - (e) => { - assert.strictEqual(e instanceof window.DOMException, true); - assert.strictEqual(e.name, "NoModificationAllowedError"); - assert.strictEqual(e.message, "Property color can not be modified."); - return true; - } - ); - }); - it("sets internals for Element", () => { const node = { nodeType: 1, style: {}, ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } - } - }; - const style = new CSSStyleDeclaration(null, { - context: node - }); - style.cssText = "color: green"; - assert.strictEqual(style.cssText, "color: green;"); - }); - - it("sets empty string for invalid cssText", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } - } - }; - const style = new CSSStyleDeclaration(null, { - context: node - }); - style.cssText = "color: green!"; - assert.strictEqual(style.cssText, ""); - }); - - it("sets only the valid properties for partially valid cssText", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } + defaultView: window } }; - const style = new CSSStyleDeclaration(null, { - context: node - }); - - // valid property followed by invalid property followed by valid property - style.cssText = "color: green; color: invalid!; background: blue;"; - // ignores invalid properties - assert.strictEqual(style.cssText, "color: green; background: blue;"); - - // only valid properties - style.cssText = "color: olivedrab; color: peru; background: bisque;"; - // keeps the last one of the same property - assert.strictEqual(style.cssText, "color: peru; background: bisque;"); - - // valid property followed by a nested selector rule - style.cssText = "color: olivedrab; &.d { color: peru; }"; - // ignores the nested selector rule - assert.strictEqual(style.cssText, "color: olivedrab;"); - - // valid property followed by a nested selector rule followed by two valid properties and an invalid property - style.cssText = - "color: olivedrab; &.d { color: peru; } color: green; background: red; invalid: rule;"; - // ignores the property immediately after the nested rule - assert.strictEqual(style.cssText, "color: olivedrab; background: red;"); - - // valid property followed by a at-rule followed by a valid property - style.cssText = "color: blue; @media screen { color: red; } color: orange;"; - // includes the the property immediately after an at-rule - assert.strictEqual(style.cssText, "color: orange;"); - - // valid property followed by a nested rule, two at-rules and two valid properties - style.cssText = ` - color: blue; - &.d { color: peru; } - @media screen { color: red; } - @layer { color: black; } - color: pink; - background: orange;`; - // ignores the first property found after the nested selector rule along with the at-rules - assert.strictEqual(style.cssText, "color: blue; background: orange;"); - }); - - it("sets internals for Element", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: { - DOMException: globalThis.DOMException - } - } + let callCount = 0; + const callback = () => { + callCount++; }; - const style = new CSSStyleDeclaration(null, { - context: node + const style = new CSSStyleDeclaration(window, { + context: node, + onChange: callback }); - style.cssText = "color: light-dark(#008000, #0000ff)"; - assert.strictEqual(style.cssText, "color: light-dark(rgb(0, 128, 0), rgb(0, 0, 255));"); + style.cssText = "color: green;"; + assert.strictEqual(callCount, 1); }); it("sets internals for CSSRule", () => { @@ -231,1422 +55,149 @@ describe("CSSStyleDeclaration", () => { parentStyleSheet: { ownerDocument: { defaultView: { - DOMException: globalThis.DOMException + DOMException: window.DOMException } } } }; - const style = new CSSStyleDeclaration(null, { + const style = new CSSStyleDeclaration(window, { context: rule }); - style.cssText = "color: green"; - assert.strictEqual(style.cssText, ""); - }); - - it("from style string", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; - assert.strictEqual(style.length, 4); - assert.strictEqual( - style.cssText, - "color: blue; background-color: red; width: 78%; height: 50vh;" - ); - assert.strictEqual(style.getPropertyValue("color"), "blue"); - assert.strictEqual(style.item(0), "color"); - assert.strictEqual(style[1], "background-color"); - assert.strictEqual(style.backgroundColor, "red"); - style.cssText = ""; - assert.strictEqual(style.cssText, ""); - assert.strictEqual(style.length, 0); - }); - - it("from properties", () => { - const style = new CSSStyleDeclaration(); - style.color = "blue"; - assert.strictEqual(style.length, 1); - assert.strictEqual(style[0], "color"); - assert.strictEqual(style.cssText, "color: blue;"); - assert.strictEqual(style.item(0), "color"); - assert.strictEqual(style.color, "blue"); - style.backgroundColor = "red"; - assert.strictEqual(style.length, 2); - assert.strictEqual(style[0], "color"); - assert.strictEqual(style[1], "background-color"); - assert.strictEqual(style.cssText, "color: blue; background-color: red;"); - assert.strictEqual(style.backgroundColor, "red"); - style.removeProperty("color"); - assert.strictEqual(style[0], "background-color"); - }); - - it("shorthand properties", () => { - const style = new CSSStyleDeclaration(); - style.background = "blue url(http://www.example.com/some_img.jpg)"; - assert.strictEqual(style.backgroundColor, "blue"); - assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); - assert.strictEqual(style.background, 'url("http://www.example.com/some_img.jpg") blue'); - style.border = "0 solid black"; - assert.strictEqual(style.borderWidth, "0px"); - assert.strictEqual(style.borderStyle, "solid"); - assert.strictEqual(style.borderColor, "black"); - assert.strictEqual(style.borderTopWidth, "0px"); - assert.strictEqual(style.borderLeftStyle, "solid"); - assert.strictEqual(style.borderBottomColor, "black"); - style.font = "12em monospace"; - assert.strictEqual(style.fontSize, "12em"); - assert.strictEqual(style.fontFamily, "monospace"); - }); - - it("width and height properties and null and empty strings", () => { - const style = new CSSStyleDeclaration(); - style.height = 6; - assert.strictEqual(style.height, ""); - style.width = 0; - assert.strictEqual(style.width, "0px"); - style.height = "34%"; - assert.strictEqual(style.height, "34%"); - style.height = "100vh"; - assert.strictEqual(style.height, "100vh"); - style.height = "100vw"; - assert.strictEqual(style.height, "100vw"); - style.height = ""; - assert.strictEqual(style.length, 1); - assert.strictEqual(style.cssText, "width: 0px;"); - style.width = null; - assert.strictEqual(style.length, 0); - assert.strictEqual(style.cssText, ""); - }); - - it("implicit properties", () => { - const style = new CSSStyleDeclaration(); - style.borderWidth = 0; - assert.strictEqual(style.border, ""); - assert.strictEqual(style.borderWidth, "0px"); - assert.strictEqual(style.borderTopWidth, "0px"); - assert.strictEqual(style.borderBottomWidth, "0px"); - assert.strictEqual(style.borderLeftWidth, "0px"); - assert.strictEqual(style.borderRightWidth, "0px"); - assert.strictEqual(style.cssText, "border-width: 0px;"); - }); - - it("top, left, right, bottom properties", () => { - const style = new CSSStyleDeclaration(); - style.top = 0; - style.left = "0%"; - style.right = "5em"; - style.bottom = "12pt"; - assert.strictEqual(style.top, "0px"); - assert.strictEqual(style.left, "0%"); - assert.strictEqual(style.right, "5em"); - assert.strictEqual(style.bottom, "12pt"); - assert.strictEqual(style.length, 4); - assert.strictEqual(style.cssText, "top: 0px; left: 0%; right: 5em; bottom: 12pt;"); - }); - - it('top, left, right, bottom properties should accept "auto"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; - assert.strictEqual(style.top, "auto"); - assert.strictEqual(style.right, "auto"); - assert.strictEqual(style.bottom, "auto"); - assert.strictEqual(style.left, "auto"); - }); - - it("clear and clip properties", () => { - const style = new CSSStyleDeclaration(); - style.clear = "none"; - assert.strictEqual(style.clear, "none"); - style.clear = "lfet"; - assert.strictEqual(style.clear, "none"); - style.clear = "left"; - assert.strictEqual(style.clear, "left"); - style.clear = "right"; - assert.strictEqual(style.clear, "right"); - style.clear = "both"; - assert.strictEqual(style.clear, "both"); - style.clip = "elipse(5px, 10px)"; - assert.strictEqual(style.clip, ""); - assert.strictEqual(style.length, 1); - style.clip = "rect(0, 3Em, 2pt, 50%)"; - assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 50%)"); - assert.strictEqual(style.length, 2); - assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); - }); - - it("colors", () => { - const style = new CSSStyleDeclaration(); - style.color = "rgba(0,0,0,0)"; - assert.strictEqual(style.color, "rgba(0, 0, 0, 0)"); - style.color = "rgba(5%, 10%, 20%, 0.4)"; - assert.strictEqual(style.color, "rgba(13, 26, 51, 0.4)"); - style.color = "rgb(33%, 34%, 33%)"; - assert.strictEqual(style.color, "rgb(84, 87, 84)"); - style.color = "rgba(300, 200, 100, 1.5)"; - assert.strictEqual(style.color, "rgb(255, 200, 100)"); - style.color = "hsla(0, 1%, 2%, 0.5)"; - assert.strictEqual(style.color, "rgba(5, 5, 5, 0.5)"); - style.color = "hsl(0, 1%, 2%)"; - assert.strictEqual(style.color, "rgb(5, 5, 5)"); - style.color = "rebeccapurple"; - assert.strictEqual(style.color, "rebeccapurple"); - style.color = "transparent"; - assert.strictEqual(style.color, "transparent"); - style.color = "currentcolor"; - assert.strictEqual(style.color, "currentcolor"); - style.color = "#ffffffff"; - assert.strictEqual(style.color, "rgb(255, 255, 255)"); - style.color = "#fffa"; - assert.strictEqual(style.color, "rgba(255, 255, 255, 0.667)"); - style.color = "#ffffff66"; - assert.strictEqual(style.color, "rgba(255, 255, 255, 0.4)"); - }); - - it("invalid hex color value", () => { - const style = new CSSStyleDeclaration(); - style.color = "#1234567"; - assert.strictEqual(style.color, ""); - }); - - it("shorthand properties with embedded spaces", () => { - let style = new CSSStyleDeclaration(); - style.background = "rgb(0, 0, 0) url(/something/somewhere.jpg)"; - assert.strictEqual(style.backgroundColor, "rgb(0, 0, 0)"); - assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")'); - assert.strictEqual(style.cssText, 'background: url("/something/somewhere.jpg") rgb(0, 0, 0);'); - style = new CSSStyleDeclaration(); - style.border = " 1px solid black "; - assert.strictEqual(style.border, "1px solid black"); - }); - - it("setting shorthand properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.borderWidth = "1px"; - assert.strictEqual(style.cssText, "border-width: 1px;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("setting implicit properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderWidth = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.borderTopWidth = "2px"; - assert.strictEqual(style.cssText, "border-top-width: 2px;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - style.borderTop = "2px solid black"; - assert.strictEqual(style.cssText, "border-top: 2px solid black;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("set border as none", () => { - const style = new CSSStyleDeclaration(); - style.border = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border as none", () => { - const style = new CSSStyleDeclaration(); - style.border = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border-style as none", () => { - const style = new CSSStyleDeclaration(); - style.borderStyle = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual(style.cssText, "border-style: none;", "cssText"); - }); - - it("set border-top as none", () => { - const style = new CSSStyleDeclaration(); - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual(style.cssText, "border-top: medium;", "cssText"); - }); - - it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "1px", "border"); - assert.strictEqual(style.borderWidth, "1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "1px", "border-top"); - assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: 1px;", "cssText"); - }); - - it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "1px", "border"); - assert.strictEqual(style.borderWidth, "1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "1px", "border-top"); - assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: 1px;", "cssText"); - }); - - it("set border as 1px and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as 1px solid and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none solid solid; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as none and change border-style to null", () => { - const style = new CSSStyleDeclaration(); - style.border = "none"; - style.borderStyle = null; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as solid and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium; border-style: none solid solid; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as solid and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.border = "solid"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border-style as solid and change border-top to none", () => { - const style = new CSSStyleDeclaration(); - style.borderStyle = "solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual( - style.cssText, - "border-style: none solid solid; border-top-width: medium; border-top-color: currentcolor;", - "cssText" - ); - }); - - it("set border-top as solid and change border-style to none", () => { - const style = new CSSStyleDeclaration(); - style.borderTop = "solid"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual( - style.cssText, - "border-top-width: medium; border-top-color: currentcolor; border-style: none;", - "cssText" - ); - }); - - it("set border-style as solid and change border-top to null", () => { - const style = new CSSStyleDeclaration(); - style.borderStyle = "solid"; - style.borderTop = null; - assert.strictEqual( - style.cssText, - "border-right-style: solid; border-bottom-style: solid; border-left-style: solid;", - "cssText" - ); - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - }); - - it("setting border values to none should change dependent values", () => { - const style = new CSSStyleDeclaration(); - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.border = "none"; - assert.strictEqual(style.border, "medium"); - assert.strictEqual(style.borderTop, "medium"); - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "medium"); - assert.strictEqual(style.cssText, "border: medium;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderStyle = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - assert.strictEqual(style.cssText, "border-top-width: 1px; border-style: none;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderTop = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "medium"); - assert.strictEqual(style.cssText, "border-top: medium;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderTopStyle = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - assert.strictEqual(style.cssText, "border-top-width: 1px; border-top-style: none;"); - - style.border = null; - style.borderImage = null; - style.border = "1px"; - assert.strictEqual(style.cssText, "border: 1px;"); - assert.strictEqual(style.border, "1px"); - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - style.borderTop = "none"; - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;" - ); - }); - - it("setting border to green", () => { - const style = new CSSStyleDeclaration(); - style.border = "green"; - assert.strictEqual(style.cssText, "border: green;"); - assert.strictEqual(style.border, "green"); - }); - - it("setting border to green", () => { - const style = new CSSStyleDeclaration(); - style.border = "green"; - assert.strictEqual(style.cssText, "border: green;"); - assert.strictEqual(style.border, "green"); - }); - - it("setting border to initial should set all properties initial", () => { - const style = new CSSStyleDeclaration(); - style.border = "initial"; - assert.strictEqual(style.cssText, "border: initial;"); - assert.strictEqual(style.border, "initial"); - assert.strictEqual(style.borderWidth, "initial"); - assert.strictEqual(style.borderStyle, "initial"); - assert.strictEqual(style.borderColor, "initial"); - assert.strictEqual(style.borderTop, "initial"); - assert.strictEqual(style.borderTopWidth, "initial"); - assert.strictEqual(style.borderTopStyle, "initial"); - assert.strictEqual(style.borderTopColor, "initial"); - assert.strictEqual(style.borderImage, "none"); - }); - - it("setting borderTop to initial should set top related properties initial", () => { - const style = new CSSStyleDeclaration(); - style.borderTop = "initial"; - assert.strictEqual(style.cssText, "border-top: initial;"); - assert.strictEqual(style.border, ""); - assert.strictEqual(style.borderWidth, ""); - assert.strictEqual(style.borderStyle, ""); - assert.strictEqual(style.borderColor, ""); - assert.strictEqual(style.borderTop, "initial"); - assert.strictEqual(style.borderTopWidth, "initial"); - assert.strictEqual(style.borderTopStyle, "initial"); - assert.strictEqual(style.borderTopColor, "initial"); - assert.strictEqual(style.borderImage, ""); - }); - - it("setting border to 0 should be okay", () => { - const style = new CSSStyleDeclaration(); - style.border = 0; - assert.strictEqual(style.cssText, "border: 0px;"); - assert.strictEqual(style.border, "0px"); - }); - - it("setting borderColor to var() should be okay", () => { - const style = new CSSStyleDeclaration(); - style.borderColor = "var(--foo)"; - assert.strictEqual(style.cssText, "border-color: var(--foo);"); - }); - - it("setting borderColor to inherit should be okay", () => { - const style = new CSSStyleDeclaration(); - style.borderColor = "inherit"; - assert.strictEqual(style.cssText, "border-color: inherit;"); - }); - - it("setting values implicit and shorthand properties via csstext and setproperty should propagate to dependent properties", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border: 1px solid black;"; - assert.strictEqual(style.cssText, "border: 1px solid black;"); - assert.strictEqual(style.borderTop, "1px solid black"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - style.setProperty("border", "1px solid black"); - assert.strictEqual(style.cssText, "border: 1px solid black;"); - }); - - it("setting opacity should work", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("opacity", 0.75); - assert.strictEqual(style.cssText, "opacity: 0.75;"); - style.opacity = "0.50"; - assert.strictEqual(style.cssText, "opacity: 0.5;"); - style.opacity = 1; - assert.strictEqual(style.cssText, "opacity: 1;"); - }); - - it("width and height of auto should work", () => { - let style = new CSSStyleDeclaration(); - style.width = "auto"; - assert.strictEqual(style.cssText, "width: auto;"); - assert.strictEqual(style.width, "auto"); - style = new CSSStyleDeclaration(); - style.height = "auto"; - assert.strictEqual(style.cssText, "height: auto;"); - assert.strictEqual(style.height, "auto"); - }); - - it("Shorthand serialization with just longhands", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;"; - assert.strictEqual(style.cssText, "margin: 10px;"); - assert.strictEqual(style.margin, "10px"); - - style.cssText = - "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;"; - assert.strictEqual( - style.cssText, - "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;" - ); - assert.strictEqual(style.margin, ""); - - style.cssText = - "margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;"; - assert.strictEqual(style.cssText, "margin: 10px !important;"); - assert.strictEqual(style.margin, "10px"); + assert.deepEqual(style.parentRule, rule); }); - it("padding and margin should set/clear shorthand properties", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - const testParts = function (name, v, V) { - style[name] = v; - for (let i = 0; i < 4; i++) { - const part = name + parts[i]; - assert.strictEqual(style[part], V[i]); - } - - assert.strictEqual(style[name], v); - style[name] = ""; - }; - testParts("padding", "1px", ["1px", "1px", "1px", "1px"]); - testParts("padding", "1px 2%", ["1px", "2%", "1px", "2%"]); - testParts("padding", "1px 2px 3px", ["1px", "2px", "3px", "2px"]); - testParts("padding", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); - style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = "1px"; - testParts("padding", "", ["", "", "", ""]); - testParts("margin", "1px", ["1px", "1px", "1px", "1px"]); - testParts("margin", "1px auto", ["1px", "auto", "1px", "auto"]); - testParts("margin", "1px 2% 3px", ["1px", "2%", "3px", "2%"]); - testParts("margin", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); - style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = "1px"; - testParts("margin", "", ["", "", "", ""]); - }); - - it("padding and margin shorthands should set main properties", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - const testParts = function (name, v, V) { - let expected; - for (let i = 0; i < 4; i++) { - style[name] = v; - style[name + parts[i]] = V; - expected = v.split(/ /); - expected[i] = V; - expected = expected.join(" "); - - assert.strictEqual(style[name], expected); - } - }; - testParts("padding", "1px 2px 3px 4px", "10px"); - testParts("margin", "1px 2px 3px 4px", "10px"); - testParts("margin", "1px 2px 3px 4px", "auto"); - }); - - it("setting individual padding and margin properties to an empty string should clear them", () => { - const style = new CSSStyleDeclaration(); - - const properties = ["padding", "margin"]; - const parts = ["Top", "Right", "Bottom", "Left"]; - for (let i = 0; i < properties.length; i++) { - for (let j = 0; j < parts.length; j++) { - const property = properties[i] + parts[j]; - style[property] = "12px"; - assert.strictEqual(style[property], "12px"); - - style[property] = ""; - assert.strictEqual(style[property], ""); - } - } - }); - - it("removing and setting individual margin properties updates the combined property accordingly", () => { - const style = new CSSStyleDeclaration(); - style.margin = "1px 2px 3px 4px"; - style.marginTop = ""; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginBottom, "3px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginBottom = ""; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginBottom = "5px"; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginBottom, "5px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginTop = "6px"; - assert.strictEqual(style.cssText, "margin: 6px 2px 5px 4px;"); - }); - - for (const property of ["padding", "margin"]) { - it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - const partValues = ["1px", "2px", "3px", "4px"]; - - for (let j = 0; j < parts.length; j++) { - const partToRemove = parts[j]; - style[property] = partValues.join(" "); - style[property + partToRemove] = ""; - - // Main property should have been removed - assert.strictEqual(style[property], ""); - - // Expect other parts to still be there - for (let k = 0; k < parts.length; k++) { - const propertyCss = `${property}-${parts[k].toLowerCase()}: ${partValues[k]};`; - if (k === j) { - assert.strictEqual(style[property + parts[k]], ""); - assert.strictEqual(style.cssText.includes(propertyCss), false); - } else { - assert.strictEqual(style[property + parts[k]], partValues[k]); - assert.strictEqual(style.cssText.includes(propertyCss), true); - } - } - } - }); - - it(`setting additional ${property} properties keeps important status of others`, () => { - const style = new CSSStyleDeclaration(); - const importantProperty = `${property}-top: 3px !important;`; - style.cssText = importantProperty; - assert.strictEqual(style.cssText.includes(importantProperty), true); - - style[`${property}Right`] = "4px"; - style[`${property}Bottom`] = "5px"; - style[`${property}Left`] = "6px"; - assert.strictEqual(style.cssText.includes(importantProperty), true); - assert.strictEqual(style.cssText.includes(`${property}-right: 4px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-bottom: 5px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-left: 6px;`), true); - assert.strictEqual(style.cssText.includes("margin:"), false); - }); - - it(`setting individual ${property} keeps important status of others`, () => { - const style = new CSSStyleDeclaration(); - style.cssText = `${property}: 3px !important;`; - style[`${property}Top`] = "4px"; - assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); - assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); - assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); - assert.strictEqual(style.cssText.includes("margin:"), false); + it("has format in internal options", () => { + const style = new CSSStyleDeclaration(window, { + foo: "bar" }); - } - - it("setting a value to 0 should return the string value", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("fill-opacity", 0); - assert.strictEqual(style.fillOpacity, "0"); - }); - - it("onchange callback should be called when the csstext changes", () => { - let called = 0; - const style = new CSSStyleDeclaration(function (cssText) { - called++; - assert.strictEqual(cssText, "opacity: 0;"); + assert.deepEqual(style._options, { + format: "specifiedValue" }); - style.cssText = "opacity: 0;"; - assert.strictEqual(called, 1); - style.cssText = "opacity: 0;"; - assert.strictEqual(called, 2); }); - it("onchange callback should be called only once when multiple properties were added", () => { - let called = 0; - const style = new CSSStyleDeclaration(function (cssText) { - called++; - assert.strictEqual(cssText, "width: 100px; height: 100px;"); + it("should not override format if exists", () => { + const style = new CSSStyleDeclaration(window, { + format: "computedValue" }); - style.cssText = "width: 100px;height:100px;"; - assert.strictEqual(called, 1); - }); - - it("onchange callback should not be called when property is set to the same value", () => { - let called = 0; - const style = new CSSStyleDeclaration(function () { - called++; + assert.deepEqual(style._options, { + format: "computedValue" }); - - style.setProperty("opacity", 0); - assert.strictEqual(called, 1); - style.setProperty("opacity", 0); - assert.strictEqual(called, 1); }); - it("onchange callback should not be called when removeProperty was called on non-existing property", () => { - let called = 0; - const style = new CSSStyleDeclaration(function () { - called++; + it("getting cssText returns empty string if computed flag is set", () => { + const style = new CSSStyleDeclaration(window, { + format: "computedValue" }); - style.removeProperty("opacity"); - assert.strictEqual(called, 0); - }); - - it("setting float should work the same as cssfloat", () => { - const style = new CSSStyleDeclaration(); - style.float = "left"; - assert.strictEqual(style.cssFloat, "left"); + style.cssText = "color: red;"; + assert.strictEqual(style.cssText, ""); }); - it("setting improper css to csstext should not throw", () => { - const style = new CSSStyleDeclaration(); + it("setting improper css to cssText should not throw", () => { + const style = new CSSStyleDeclaration(window); style.cssText = "color: "; assert.strictEqual(style.cssText, ""); - style.color = "black"; - style.cssText = "float: "; + style.cssText = "color: red!"; assert.strictEqual(style.cssText, ""); }); - it("url parsing works with quotes", () => { - const style = new CSSStyleDeclaration(); - style.backgroundImage = "url(http://some/url/here1.png)"; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); - style.backgroundImage = "url('http://some/url/here2.png')"; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); - style.backgroundImage = 'url("http://some/url/here3.png")'; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); - }); - - it("setting 0 to a padding or margin works", () => { - const style = new CSSStyleDeclaration(); - style.padding = 0; - assert.strictEqual(style.cssText, "padding: 0px;"); - style.margin = "1em"; - style.marginTop = "0"; - assert.strictEqual(style.marginTop, "0px"); - }); - - it("setting ex units to a padding or margin works", () => { - const style = new CSSStyleDeclaration(); - style.padding = "1ex"; - assert.strictEqual(style.cssText, "padding: 1ex;"); - style.margin = "1em"; - style.marginTop = "0.5ex"; - assert.strictEqual(style.marginTop, "0.5ex"); - }); - - it("setting empty string and null to a padding or margin works", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - function testParts(base, nullValue) { - const props = [base].concat(parts.map((part) => base + part)); - for (const prop of props) { - assert.strictEqual(style[prop], ""); - style[prop] = "10px"; - assert.strictEqual(style[prop], "10px"); - style[prop] = nullValue; - assert.strictEqual(style[prop], ""); - } - } - - testParts("margin", ""); - testParts("margin", null); - testParts("padding", ""); - testParts("padding", null); - }); - - it("setting undefined to a padding or margin does nothing", () => { - const style = new CSSStyleDeclaration(); - const parts = ["Top", "Right", "Bottom", "Left"]; - function testParts(base) { - const props = [base].concat(parts.map((part) => base + part)); - for (const prop of props) { - style[prop] = "10px"; - assert.strictEqual(style[prop], "10px"); - style[prop] = undefined; - assert.strictEqual(style[prop], "10px"); + it("item() throws if argument is not given", () => { + const style = new CSSStyleDeclaration(window); + assert.throws( + () => { + style.item(); + }, + (e) => { + assert.strictEqual(e instanceof window.TypeError, true); + assert.strictEqual(e.message, "1 argument required, but only 0 present."); + return true; } - } - - testParts("margin"); - testParts("padding"); - }); - - it("setting null to background works", () => { - const style = new CSSStyleDeclaration(); - style.background = "red"; - assert.strictEqual(style.cssText, "background: red;"); - style.background = null; - assert.strictEqual(style.cssText, ""); - }); - - it("flex properties should keep their values", () => { - const style = new CSSStyleDeclaration(); - style.flexDirection = "column"; - assert.strictEqual(style.cssText, "flex-direction: column;"); - style.flexDirection = "row"; - assert.strictEqual(style.cssText, "flex-direction: row;"); + ); }); - it("camelcase properties are not assigned with `.setproperty()`", () => { - const style = new CSSStyleDeclaration(); + it("camelcase properties are not assigned with setproperty()", () => { + const style = new CSSStyleDeclaration(window); style.setProperty("fontSize", "12px"); assert.strictEqual(style.cssText, ""); }); - it("casing is ignored in `.setproperty()`", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("FoNt-SiZe", "12px"); - assert.strictEqual(style.fontSize, "12px"); - assert.strictEqual(style.getPropertyValue("font-size"), "12px"); - }); - - it("support non string entries in border-spacing", () => { - const style = new CSSStyleDeclaration(); - style.borderSpacing = 0; - assert.strictEqual(style.cssText, "border-spacing: 0px;"); - }); - - it("float should be valid property for `.setproperty()`", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("float", "left"); - assert.strictEqual(style.float, "left"); - assert.strictEqual(style.getPropertyValue("float"), "left"); - }); - - it("flex-shrink works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex-shrink", 0); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - style.setProperty("flex-shrink", 1); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.cssText, "flex-shrink: 1;"); - }); - - it("flex-grow works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex-grow", 2); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.cssText, "flex-grow: 2;"); - }); - - it("flex-basis works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex-basis", 0); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); - style.setProperty("flex-basis", "250px"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); - style.setProperty("flex-basis", "10em"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "10em"); - style.setProperty("flex-basis", "30%"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "30%"); - assert.strictEqual(style.cssText, "flex-basis: 30%;"); - }); - - it("shorthand flex works", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("flex", "none"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.removeProperty("flex-basis"); - style.setProperty("flex", "auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "0 1 250px"); - assert.strictEqual(style.getPropertyValue("flex"), "0 1 250px"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); - style.removeProperty("flex"); - style.setProperty("flex", "0 0 auto"); - assert.strictEqual(style.getPropertyValue("flex"), "0 0 auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "0 auto"); - assert.strictEqual(style.getPropertyValue("flex"), "0 1 auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "2"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); - style.removeProperty("flex"); - style.setProperty("flex", "20%"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "20%"); - style.removeProperty("flex"); - style.setProperty("flex", "2 2"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "2"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); - style.removeProperty("flex"); - }); + it("custom properties are case-sensitive", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "--fOo: purple"; - it("font-size get a valid value", () => { - const style = new CSSStyleDeclaration(); - const invalidValue = "1r5px"; - style.cssText = "font-size: 15px"; - assert.strictEqual(1, style.length); - style.cssText = `font-size: ${invalidValue}`; - assert.strictEqual(0, style.length); - assert.strictEqual(undefined, style[0]); + assert.strictEqual(style.getPropertyValue("--foo"), ""); + assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); }); it("getPropertyValue for custom properties in cssText", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.cssText = "--foo: red"; assert.strictEqual(style.getPropertyValue("--foo"), "red"); }); it("getPropertyValue for custom properties with setProperty", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style.setProperty("--bar", "blue"); assert.strictEqual(style.getPropertyValue("--bar"), "blue"); }); it("getPropertyValue for custom properties with object setter", () => { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleDeclaration(window); style["--baz"] = "yellow"; assert.strictEqual(style.getPropertyValue("--baz"), ""); }); - it("custom properties are case-sensitive", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "--fOo: purple"; - - assert.strictEqual(style.getPropertyValue("--foo"), ""); - assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); - }); - - for (const property of [ - "width", - "height", - "margin", - "margin-top", - "bottom", - "right", - "padding" - ]) { - it(`supports calc for ${property}`, () => { - const style = new CSSStyleDeclaration(); - style.setProperty(property, "calc(100% - 100px)"); - assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); - }); - } - - it("supports nested calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "calc(100% - calc(200px - 100px))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); - }); - - it("supports nested calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "calc(100% * calc(2 / 3))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); - }); - - it("supports var", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo)"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); - }); - - it("supports var with fallback", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo, 100px)"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); + it("getPropertyPriority for property", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("color", "green", "important"); + assert.strictEqual(style.getPropertyPriority("color"), "important"); }); - it("supports var with var fallback", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo, var(--bar))"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, var(--bar))"); + it("getPropertyPriority for custom property", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green", "important"); + assert.strictEqual(style.getPropertyPriority("--foo"), "important"); }); - it("supports calc with var inside", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "calc(100% - var(--foo))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(100% - var(--foo))"); - }); - - it("supports var with calc inside", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "var(--foo, calc(var(--bar) + 3px))"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, calc(var(--bar) + 3px))"); - }); - - it("supports color var", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("color", "var(--foo)"); - assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); - }); - - it("should not normalize if var() is included", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); - assert.strictEqual( - style.getPropertyValue("line-height"), - "calc( /* comment */ 100% - calc(var(--foo) *2 ))" - ); - }); - - it("supports abs", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "abs(1 - 2 * 3)"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(5)"); - }); - - it("supports abs inside calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "calc(abs(1) + abs(2))"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); - }); - - it("supports sign", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "sign(.1)"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); - }); - - it("supports sign inside calc", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("line-height", "calc(sign(.1) + sign(.2))"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(2)"); - }); - - it("no-op for setting undefined to width", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "10px"); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.setProperty("width", undefined); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.width = undefined; - assert.strictEqual(style.getPropertyValue("width"), "10px"); - }); - - it("shorthand serialization with shorthand and longhands mixed", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: blue; background: red !important; background-color: green;"; - assert.strictEqual(style.cssText, "background: red !important;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleDeclaration(); - style.cssText = - "border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;"; - assert.strictEqual(style.cssText, "border: 1px;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border-width: 1px;"; - assert.strictEqual(style.cssText, "border-width: 1px;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border: 1px; border-top: 1px !important;"; - assert.strictEqual( - style.cssText, - "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;" - ); - }); - - it("set cssText as none", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "border: none;"; - assert.strictEqual(style.cssText, "border: medium;"); - }); - - it("invalid cssText should be parsed", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "color: red; }"; - assert.strictEqual(style.cssText, "color: red;"); - }); - - it("single value flex with CSS-wide keyword", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial;"; - assert.strictEqual(style.flex, "initial"); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "initial"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex: initial;"); - }); - - it("single value flex with non-CSS-wide value", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: 0;"; - assert.strictEqual(style.flex, "0 1 0%"); - assert.strictEqual(style.flexGrow, "0"); - assert.strictEqual(style.flexShrink, "1"); - assert.strictEqual(style.flexBasis, "0%"); - assert.strictEqual(style.cssText, "flex: 0 1 0%;"); - }); - - it("multiple values flex with CSS-wide keyword", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial; flex-basis: initial; flex-shrink: initial;"; - assert.strictEqual(style.flex, "initial"); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "initial"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex: initial;"); - }); - - it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial; flex-shrink: 0;"; - assert.strictEqual(style.flex, ""); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "0"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;"); - }); - - it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { - const style = new CSSStyleDeclaration(); - style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; - assert.strictEqual(style.flex, ""); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "2"); - assert.strictEqual(style.flexBasis, "0px"); - assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;"); - }); -}); - -/* regression tests */ -describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { - it("should set global value unset", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("width", "10px"); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.setProperty("width", "unset"); - assert.strictEqual(style.getPropertyValue("width"), "unset"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { - it("should not set custom properties twice", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("--foo", 0); - style.setProperty("--foo", 1); - - assert.strictEqual(style.length, 1); - assert.strictEqual(style.item(0), "--foo"); - assert.strictEqual(style.item(1), ""); - assert.deepEqual(JSON.parse(JSON.stringify(style)), { - 0: "--foo" + it("setProperty throws if read-only flag is set", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green"); + style.setOptions({ + readOnly: true }); - assert.strictEqual(style.getPropertyValue("--foo"), "1"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { - it("should set stringified value", () => { - const style = new CSSStyleDeclaration(); - style.setProperty("--foo", true); - assert.strictEqual(style.getPropertyValue("--foo"), "true"); - }); - - it("throws for setting Symbol", () => { - const style = new CSSStyleDeclaration(); assert.throws( - () => style.setProperty("width", Symbol("foo")), + () => { + style.setProperty("--foo", "red"); + }, (e) => { - assert.strictEqual(e instanceof TypeError, true); - assert.strictEqual(e.message, "Can not convert symbol to string."); + assert.strictEqual(e instanceof window.DOMException, true); + assert.strictEqual(e.name, "NoModificationAllowedError"); + assert.strictEqual(e.message, "Property --foo can not be modified."); return true; } ); + }); + + it("removeProperty throws if read-only flag is set", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", "green"); + style.setProperty("--bar", "red"); + assert.strictEqual(style.removeProperty("--foo"), "green"); + style.setOptions({ + readOnly: true + }); assert.throws( () => { - style.width = Symbol("foo"); + style.removeProperty("--bar"); }, (e) => { - assert.strictEqual(e instanceof TypeError, true); - assert.strictEqual(e.message, "Can not convert symbol to string."); + assert.strictEqual(e instanceof window.DOMException, true); + assert.strictEqual(e.name, "NoModificationAllowedError"); + assert.strictEqual(e.message, "Property --bar can not be modified."); return true; } ); }); }); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { - it('returns empty string for "webkit-*", without leading "-"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: green; webkit-transform: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitTransform, ""); - }); - - it('should set/get value for "-webkit-*"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: green; -webkit-transform: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitTransform, "scale(3)"); - }); - - it('returns undefined for unknown "-webkit-*"', () => { - const style = new CSSStyleDeclaration(); - style.cssText = "background-color: green; -webkit-foo: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitFoo, undefined); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { - it("no-op when setting undefined to border", () => { - const style = new CSSStyleDeclaration(); - style.border = "1px solid green"; - assert.strictEqual(style.border, "1px solid green"); - style.border = undefined; - assert.strictEqual(style.border, "1px solid green"); - }); - - it("no-op when setting undefined to borderWidth", () => { - const style = new CSSStyleDeclaration(); - style.borderWidth = "1px"; - assert.strictEqual(style.borderWidth, "1px"); - style.border = undefined; - assert.strictEqual(style.borderWidth, "1px"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () => { - it("should support keywords", () => { - const keywords = [ - "serif", - "sans-serif", - "cursive", - "fantasy", - "monospace", - "system-ui", - "math", - "ui-serif", - "ui-sans-serif", - "ui-monospace", - "ui-rounded" - ]; - const style = new CSSStyleDeclaration(); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - it("should support generic() function keywords", () => { - const keywords = [ - "generic(fangsong)", - "generic(kai)", - "generic(khmer-mul)", - "generic(nastaliq)" - ]; - const style = new CSSStyleDeclaration(); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - // see https://drafts.csswg.org/css-fonts-4/#changes-2021-12-21 - it("should support removed generic keywords as non generic family name", () => { - const keywords = ["emoji", "fangsong"]; - const style = new CSSStyleDeclaration(); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - it("should support `-webkit-` prefixed family name", () => { - const style = new CSSStyleDeclaration(); - style.fontFamily = "-webkit-body"; - assert.strictEqual(style.fontFamily, "-webkit-body"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { - it("should get normalized value for font shorthand", () => { - const style = new CSSStyleDeclaration(); - style.font = "normal bold 4px sans-serif"; - assert.strictEqual(style.font, "bold 4px sans-serif"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { - it("should return value for each property", () => { - const style = new CSSStyleDeclaration(); - const key = "background-color"; - const camel = "backgroundColor"; - const value = "var(--foo)"; - style[key] = value; - assert.strictEqual(style[key], value); - style[key] = null; - style[camel] = value; - assert.strictEqual(style[camel], value); - }); - - it("should set var() values for background-attachment correctly", () => { - const style = new CSSStyleDeclaration(); - style.backgroundAttachment = "var(--foo)"; - assert.strictEqual(style.backgroundAttachment, "var(--foo)"); - style.setProperty("background-attachment", "var(--bar)"); - assert.strictEqual(style.backgroundAttachment, "var(--bar)"); - }); -}); - -describe("propertyList", () => { - it("should get property list", () => { - assert.deepEqual(propertyList, Object.fromEntries(implementedProperties)); - }); -}); diff --git a/test/properties.test.js b/test/CSSStyleProperties.test.js similarity index 50% rename from test/properties.test.js rename to test/CSSStyleProperties.test.js index 4594780b..dd626053 100644 --- a/test/properties.test.js +++ b/test/CSSStyleProperties.test.js @@ -3,9 +3,18 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); +const { CSSStyleProperties } = require("../lib/CSSStyleProperties"); +const propertyList = require("../lib/generated/propertyList"); +const camelize = require("../scripts/camelize"); + +const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError +}; function testPropertyValue(property, value, expected) { - const style = new CSSStyleDeclaration(); + const style = new CSSStyleProperties(window); let res; style.setProperty(property, value); @@ -34,7 +43,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); @@ -118,6 +127,1425 @@ function testImplicitPropertyValue(property, value, expected, sub) { } } +describe("CSSStyleProperties", () => { + it("is instanceof CSSStyleDeclaration", () => { + const style = new CSSStyleProperties(window); + assert.strictEqual(style instanceof CSSStyleDeclaration, true); + }); + + it("all dashed properties are included in propertyList", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { + assert.strictEqual(propertyList.has(i), true, i); + } + } + }); + + it("has camelCased property for dashed property", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { + const camel = camelize.dashedToCamelCase(i); + assert.ok(style[camel] !== undefined, i); + } + } + }); + + // FIXME: https://github.com/jsdom/cssstyle/issues/210 + it.skip("all webkit prefixed properties are included in propertyList", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { + assert.strictEqual(propertyList.has(i), true, i); + } + } + }); + + it("has camelCased property for webkit prefixed property", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { + const camel = camelize.dashedToCamelCase(i); + assert.ok(style[camel] !== undefined, i); + } + } + }); + + it("has PascalCased property for webkit prefixed property", () => { + const style = new CSSStyleProperties(window); + for (const i in style) { + if (/^webkit[A-Z]/.test(i)) { + const pascal = i.replace(/^webkit/, "Webkit"); + assert.ok(style[pascal] !== undefined); + } + } + }); + + it("setting cssFloat should also set float", () => { + const style = new CSSStyleProperties(window); + style.cssFloat = "left"; + assert.strictEqual(style.cssFloat, "left"); + assert.strictEqual(style.float, "left"); + }); + + it("setting float should also set cssfloat", () => { + const style = new CSSStyleProperties(window); + style.float = "left"; + assert.strictEqual(style.cssFloat, "left"); + assert.strictEqual(style.float, "left"); + }); + + it("from style string", () => { + const style = new CSSStyleProperties(window); + style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; + assert.strictEqual(style.length, 4); + assert.strictEqual( + style.cssText, + "color: blue; background-color: red; width: 78%; height: 50vh;" + ); + assert.strictEqual(style.getPropertyValue("color"), "blue"); + assert.strictEqual(style.item(0), "color"); + assert.strictEqual(style[1], "background-color"); + assert.strictEqual(style.backgroundColor, "red"); + style.cssText = ""; + assert.strictEqual(style.cssText, ""); + assert.strictEqual(style.length, 0); + }); + + it("from properties", () => { + const style = new CSSStyleProperties(window); + style.color = "blue"; + assert.strictEqual(style.length, 1); + assert.strictEqual(style[0], "color"); + assert.strictEqual(style.cssText, "color: blue;"); + assert.strictEqual(style.item(0), "color"); + assert.strictEqual(style.color, "blue"); + style.backgroundColor = "red"; + assert.strictEqual(style.length, 2); + assert.strictEqual(style[0], "color"); + assert.strictEqual(style[1], "background-color"); + assert.strictEqual(style.cssText, "color: blue; background-color: red;"); + assert.strictEqual(style.backgroundColor, "red"); + style.removeProperty("color"); + assert.strictEqual(style[0], "background-color"); + }); + + it("ignores invalid properties", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleProperties(window, { + context: node + }); + style.cssText = "color: green; color: invalid!; background: blue;"; + // ignores invalid properties + assert.strictEqual(style.cssText, "color: green; background: blue;"); + }); + + it("keeps the last one of the same property", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleProperties(window, { + context: node + }); + // 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;"); + }); + + it("ignores the nested selector rule", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleProperties(window, { + context: node + }); + // 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;"); + }); + + it("ignores the property immediately after the nested rule", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleProperties(window, { + context: node + }); + // 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;"); + }); + + it("includes the the property immediately after an at-rule", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleProperties(window, { + context: node + }); + // 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;"); + }); + + it("ignores the first property found after the nested selector rule along with the at-rules", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleProperties(window, { + context: node + }); + // 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("shorthand properties", () => { + const style = new CSSStyleProperties(window); + style.background = "blue url(http://www.example.com/some_img.jpg)"; + assert.strictEqual(style.backgroundColor, "blue"); + assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); + assert.strictEqual(style.background, 'url("http://www.example.com/some_img.jpg") blue'); + style.border = "0 solid black"; + assert.strictEqual(style.borderWidth, "0px"); + assert.strictEqual(style.borderStyle, "solid"); + assert.strictEqual(style.borderColor, "black"); + assert.strictEqual(style.borderTopWidth, "0px"); + assert.strictEqual(style.borderLeftStyle, "solid"); + assert.strictEqual(style.borderBottomColor, "black"); + style.font = "12em monospace"; + assert.strictEqual(style.fontSize, "12em"); + assert.strictEqual(style.fontFamily, "monospace"); + }); + + it("width and height properties and null and empty strings", () => { + const style = new CSSStyleProperties(window); + style.height = 6; + assert.strictEqual(style.height, ""); + style.width = 0; + assert.strictEqual(style.width, "0px"); + style.height = "34%"; + assert.strictEqual(style.height, "34%"); + style.height = "100vh"; + assert.strictEqual(style.height, "100vh"); + style.height = "100vw"; + assert.strictEqual(style.height, "100vw"); + style.height = ""; + assert.strictEqual(style.length, 1); + assert.strictEqual(style.cssText, "width: 0px;"); + style.width = null; + assert.strictEqual(style.length, 0); + assert.strictEqual(style.cssText, ""); + }); + + it("implicit properties", () => { + const style = new CSSStyleProperties(window); + style.borderWidth = 0; + assert.strictEqual(style.border, ""); + assert.strictEqual(style.borderWidth, "0px"); + assert.strictEqual(style.borderTopWidth, "0px"); + assert.strictEqual(style.borderBottomWidth, "0px"); + assert.strictEqual(style.borderLeftWidth, "0px"); + assert.strictEqual(style.borderRightWidth, "0px"); + assert.strictEqual(style.cssText, "border-width: 0px;"); + }); + + it("top, left, right, bottom properties", () => { + const style = new CSSStyleProperties(window); + style.top = 0; + style.left = "0%"; + style.right = "5em"; + style.bottom = "12pt"; + assert.strictEqual(style.top, "0px"); + assert.strictEqual(style.left, "0%"); + assert.strictEqual(style.right, "5em"); + assert.strictEqual(style.bottom, "12pt"); + assert.strictEqual(style.length, 4); + assert.strictEqual(style.cssText, "top: 0px; left: 0%; right: 5em; bottom: 12pt;"); + }); + + it('top, left, right, bottom properties should accept "auto"', () => { + const style = new CSSStyleProperties(window); + style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; + assert.strictEqual(style.top, "auto"); + assert.strictEqual(style.right, "auto"); + assert.strictEqual(style.bottom, "auto"); + assert.strictEqual(style.left, "auto"); + }); + + it("clear and clip properties", () => { + const style = new CSSStyleProperties(window); + style.clear = "none"; + assert.strictEqual(style.clear, "none"); + style.clear = "lfet"; + assert.strictEqual(style.clear, "none"); + style.clear = "left"; + assert.strictEqual(style.clear, "left"); + style.clear = "right"; + assert.strictEqual(style.clear, "right"); + style.clear = "both"; + assert.strictEqual(style.clear, "both"); + style.clip = "elipse(5px, 10px)"; + assert.strictEqual(style.clip, ""); + assert.strictEqual(style.length, 1); + style.clip = "rect(0, 3Em, 2pt, 50%)"; + assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 50%)"); + assert.strictEqual(style.length, 2); + assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); + }); + + it("colors", () => { + const style = new CSSStyleProperties(window); + style.color = "rgba(0,0,0,0)"; + assert.strictEqual(style.color, "rgba(0, 0, 0, 0)"); + style.color = "rgba(5%, 10%, 20%, 0.4)"; + assert.strictEqual(style.color, "rgba(13, 26, 51, 0.4)"); + style.color = "rgb(33%, 34%, 33%)"; + assert.strictEqual(style.color, "rgb(84, 87, 84)"); + style.color = "rgba(300, 200, 100, 1.5)"; + assert.strictEqual(style.color, "rgb(255, 200, 100)"); + style.color = "hsla(0, 1%, 2%, 0.5)"; + assert.strictEqual(style.color, "rgba(5, 5, 5, 0.5)"); + style.color = "hsl(0, 1%, 2%)"; + assert.strictEqual(style.color, "rgb(5, 5, 5)"); + style.color = "rebeccapurple"; + assert.strictEqual(style.color, "rebeccapurple"); + style.color = "transparent"; + assert.strictEqual(style.color, "transparent"); + style.color = "currentcolor"; + assert.strictEqual(style.color, "currentcolor"); + style.color = "#ffffffff"; + assert.strictEqual(style.color, "rgb(255, 255, 255)"); + style.color = "#fffa"; + assert.strictEqual(style.color, "rgba(255, 255, 255, 0.667)"); + style.color = "#ffffff66"; + assert.strictEqual(style.color, "rgba(255, 255, 255, 0.4)"); + }); + + it("invalid hex color value", () => { + const style = new CSSStyleProperties(window); + style.color = "#1234567"; + assert.strictEqual(style.color, ""); + }); + + it("shorthand properties with embedded spaces", () => { + let style = new CSSStyleProperties(window); + style.background = "rgb(0, 0, 0) url(/something/somewhere.jpg)"; + assert.strictEqual(style.backgroundColor, "rgb(0, 0, 0)"); + assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")'); + assert.strictEqual(style.cssText, 'background: url("/something/somewhere.jpg") rgb(0, 0, 0);'); + style = new CSSStyleProperties(window); + style.border = " 1px solid black "; + assert.strictEqual(style.border, "1px solid black"); + }); + + it("setting shorthand properties to an empty string should clear all dependent properties", () => { + const style = new CSSStyleProperties(window); + style.borderWidth = "1px"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("setting implicit properties to an empty string should clear all dependent properties", () => { + const style = new CSSStyleProperties(window); + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderWidth = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { + const style = new CSSStyleProperties(window); + style.borderTopWidth = "2px"; + assert.strictEqual(style.cssText, "border-top-width: 2px;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + style.borderTop = "2px solid black"; + assert.strictEqual(style.cssText, "border-top: 2px solid black;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("set border as none", () => { + const style = new CSSStyleProperties(window); + style.border = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border as none", () => { + const style = new CSSStyleProperties(window); + style.border = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border-style as none", () => { + const style = new CSSStyleProperties(window); + style.borderStyle = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual(style.cssText, "border-style: none;", "cssText"); + }); + + it("set border-top as none", () => { + const style = new CSSStyleProperties(window); + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual(style.cssText, "border-top: medium;", "cssText"); + }); + + it("set border as 1px and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "1px", "border"); + assert.strictEqual(style.borderWidth, "1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "1px", "border-top"); + assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: 1px;", "cssText"); + }); + + it("set border as 1px and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "1px", "border"); + assert.strictEqual(style.borderWidth, "1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "1px", "border-top"); + assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: 1px;", "cssText"); + }); + + it("set border as 1px and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as 1px solid and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.border = "1px solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none solid solid; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as none and change border-style to null", () => { + const style = new CSSStyleProperties(window); + style.border = "none"; + style.borderStyle = null; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as solid and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.border = "solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium; border-style: none solid solid; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as solid and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.border = "solid"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border-style as solid and change border-top to none", () => { + const style = new CSSStyleProperties(window); + style.borderStyle = "solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual( + style.cssText, + "border-style: none solid solid; border-top-width: medium; border-top-color: currentcolor;", + "cssText" + ); + }); + + it("set border-top as solid and change border-style to none", () => { + const style = new CSSStyleProperties(window); + style.borderTop = "solid"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual( + style.cssText, + "border-top-width: medium; border-top-color: currentcolor; border-style: none;", + "cssText" + ); + }); + + it("set border-style as solid and change border-top to null", () => { + const style = new CSSStyleProperties(window); + style.borderStyle = "solid"; + style.borderTop = null; + assert.strictEqual( + style.cssText, + "border-right-style: solid; border-bottom-style: solid; border-left-style: solid;", + "cssText" + ); + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + }); + + it("setting border values to none should change dependent values", () => { + const style = new CSSStyleProperties(window); + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.border = "none"; + assert.strictEqual(style.border, "medium"); + assert.strictEqual(style.borderTop, "medium"); + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "medium"); + assert.strictEqual(style.cssText, "border: medium;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderStyle = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + assert.strictEqual(style.cssText, "border-top-width: 1px; border-style: none;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderTop = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "medium"); + assert.strictEqual(style.cssText, "border-top: medium;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderTopStyle = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + assert.strictEqual(style.cssText, "border-top-width: 1px; border-top-style: none;"); + + style.border = null; + style.borderImage = null; + style.border = "1px"; + assert.strictEqual(style.cssText, "border: 1px;"); + assert.strictEqual(style.border, "1px"); + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + style.borderTop = "none"; + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;" + ); + }); + + it("setting border to green", () => { + const style = new CSSStyleProperties(window); + style.border = "green"; + assert.strictEqual(style.cssText, "border: green;"); + assert.strictEqual(style.border, "green"); + }); + + it("setting border to green", () => { + const style = new CSSStyleProperties(window); + style.border = "green"; + assert.strictEqual(style.cssText, "border: green;"); + assert.strictEqual(style.border, "green"); + }); + + it("setting border to initial should set all properties initial", () => { + const style = new CSSStyleProperties(window); + style.border = "initial"; + assert.strictEqual(style.cssText, "border: initial;"); + assert.strictEqual(style.border, "initial"); + assert.strictEqual(style.borderWidth, "initial"); + assert.strictEqual(style.borderStyle, "initial"); + assert.strictEqual(style.borderColor, "initial"); + assert.strictEqual(style.borderTop, "initial"); + assert.strictEqual(style.borderTopWidth, "initial"); + assert.strictEqual(style.borderTopStyle, "initial"); + assert.strictEqual(style.borderTopColor, "initial"); + assert.strictEqual(style.borderImage, "none"); + }); + + it("setting borderTop to initial should set top related properties initial", () => { + const style = new CSSStyleProperties(window); + style.borderTop = "initial"; + assert.strictEqual(style.cssText, "border-top: initial;"); + assert.strictEqual(style.border, ""); + assert.strictEqual(style.borderWidth, ""); + assert.strictEqual(style.borderStyle, ""); + assert.strictEqual(style.borderColor, ""); + assert.strictEqual(style.borderTop, "initial"); + assert.strictEqual(style.borderTopWidth, "initial"); + assert.strictEqual(style.borderTopStyle, "initial"); + assert.strictEqual(style.borderTopColor, "initial"); + assert.strictEqual(style.borderImage, ""); + }); + + it("setting border to 0 should be okay", () => { + const style = new CSSStyleProperties(window); + style.border = 0; + assert.strictEqual(style.cssText, "border: 0px;"); + assert.strictEqual(style.border, "0px"); + }); + + it("setting borderColor to var() should be okay", () => { + const style = new CSSStyleProperties(window); + style.borderColor = "var(--foo)"; + assert.strictEqual(style.cssText, "border-color: var(--foo);"); + }); + + it("setting borderColor to inherit should be okay", () => { + const style = new CSSStyleProperties(window); + style.borderColor = "inherit"; + assert.strictEqual(style.cssText, "border-color: inherit;"); + }); + + it("setting values implicit and shorthand properties via csstext and setproperty should propagate to dependent properties", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border: 1px solid black;"; + assert.strictEqual(style.cssText, "border: 1px solid black;"); + assert.strictEqual(style.borderTop, "1px solid black"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + style.setProperty("border", "1px solid black"); + assert.strictEqual(style.cssText, "border: 1px solid black;"); + }); + + it("setting opacity should work", () => { + const style = new CSSStyleProperties(window); + style.setProperty("opacity", 0.75); + assert.strictEqual(style.cssText, "opacity: 0.75;"); + style.opacity = "0.50"; + assert.strictEqual(style.cssText, "opacity: 0.5;"); + style.opacity = 1; + assert.strictEqual(style.cssText, "opacity: 1;"); + }); + + it("width and height of auto should work", () => { + let style = new CSSStyleProperties(window); + style.width = "auto"; + assert.strictEqual(style.cssText, "width: auto;"); + assert.strictEqual(style.width, "auto"); + style = new CSSStyleProperties(window); + style.height = "auto"; + assert.strictEqual(style.cssText, "height: auto;"); + assert.strictEqual(style.height, "auto"); + }); + + it("Shorthand serialization with just longhands", () => { + const style = new CSSStyleProperties(window); + style.cssText = "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;"; + assert.strictEqual(style.cssText, "margin: 10px;"); + assert.strictEqual(style.margin, "10px"); + + style.cssText = + "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;"; + assert.strictEqual( + style.cssText, + "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;" + ); + assert.strictEqual(style.margin, ""); + + style.cssText = + "margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;"; + assert.strictEqual(style.cssText, "margin: 10px !important;"); + assert.strictEqual(style.margin, "10px"); + }); + + it("padding and margin should set/clear shorthand properties", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const testParts = function (name, v, V) { + style[name] = v; + for (let i = 0; i < 4; i++) { + const part = name + parts[i]; + assert.strictEqual(style[part], V[i]); + } + + assert.strictEqual(style[name], v); + style[name] = ""; + }; + testParts("padding", "1px", ["1px", "1px", "1px", "1px"]); + testParts("padding", "1px 2%", ["1px", "2%", "1px", "2%"]); + testParts("padding", "1px 2px 3px", ["1px", "2px", "3px", "2px"]); + testParts("padding", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); + style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = "1px"; + testParts("padding", "", ["", "", "", ""]); + testParts("margin", "1px", ["1px", "1px", "1px", "1px"]); + testParts("margin", "1px auto", ["1px", "auto", "1px", "auto"]); + testParts("margin", "1px 2% 3px", ["1px", "2%", "3px", "2%"]); + testParts("margin", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); + style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = "1px"; + testParts("margin", "", ["", "", "", ""]); + }); + + it("padding and margin shorthands should set main properties", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const testParts = function (name, v, V) { + let expected; + for (let i = 0; i < 4; i++) { + style[name] = v; + style[name + parts[i]] = V; + expected = v.split(/ /); + expected[i] = V; + expected = expected.join(" "); + + assert.strictEqual(style[name], expected); + } + }; + testParts("padding", "1px 2px 3px 4px", "10px"); + testParts("margin", "1px 2px 3px 4px", "10px"); + testParts("margin", "1px 2px 3px 4px", "auto"); + }); + + it("setting individual padding and margin properties to an empty string should clear them", () => { + const style = new CSSStyleProperties(window); + + const properties = ["padding", "margin"]; + const parts = ["Top", "Right", "Bottom", "Left"]; + for (let i = 0; i < properties.length; i++) { + for (let j = 0; j < parts.length; j++) { + const property = properties[i] + parts[j]; + style[property] = "12px"; + assert.strictEqual(style[property], "12px"); + + style[property] = ""; + assert.strictEqual(style[property], ""); + } + } + }); + + it("removing and setting individual margin properties updates the combined property accordingly", () => { + const style = new CSSStyleProperties(window); + style.margin = "1px 2px 3px 4px"; + style.marginTop = ""; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginBottom, "3px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginBottom = ""; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginBottom = "5px"; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginBottom, "5px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginTop = "6px"; + assert.strictEqual(style.cssText, "margin: 6px 2px 5px 4px;"); + }); + + for (const property of ["padding", "margin"]) { + it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const partValues = ["1px", "2px", "3px", "4px"]; + + for (let j = 0; j < parts.length; j++) { + const partToRemove = parts[j]; + style[property] = partValues.join(" "); + style[property + partToRemove] = ""; + + // Main property should have been removed + assert.strictEqual(style[property], ""); + + // Expect other parts to still be there + for (let k = 0; k < parts.length; k++) { + const propertyCss = `${property}-${parts[k].toLowerCase()}: ${partValues[k]};`; + if (k === j) { + assert.strictEqual(style[property + parts[k]], ""); + assert.strictEqual(style.cssText.includes(propertyCss), false); + } else { + assert.strictEqual(style[property + parts[k]], partValues[k]); + assert.strictEqual(style.cssText.includes(propertyCss), true); + } + } + } + }); + + it(`setting additional ${property} properties keeps important status of others`, () => { + const style = new CSSStyleProperties(window); + const importantProperty = `${property}-top: 3px !important;`; + style.cssText = importantProperty; + assert.strictEqual(style.cssText.includes(importantProperty), true); + + style[`${property}Right`] = "4px"; + style[`${property}Bottom`] = "5px"; + style[`${property}Left`] = "6px"; + assert.strictEqual(style.cssText.includes(importantProperty), true); + assert.strictEqual(style.cssText.includes(`${property}-right: 4px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-bottom: 5px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-left: 6px;`), true); + assert.strictEqual(style.cssText.includes("margin:"), false); + }); + + it(`setting individual ${property} keeps important status of others`, () => { + const style = new CSSStyleProperties(window); + style.cssText = `${property}: 3px !important;`; + style[`${property}Top`] = "4px"; + assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); + assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); + assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); + assert.strictEqual(style.cssText.includes("margin:"), false); + }); + } + + it("setting a value to 0 should return the string value", () => { + const style = new CSSStyleProperties(window); + style.setProperty("fill-opacity", 0); + assert.strictEqual(style.fillOpacity, "0"); + }); + + it("onchange callback should be called when the csstext changes", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: (cssText) => { + called++; + assert.strictEqual(cssText, "opacity: 0;"); + } + }); + style.cssText = "opacity: 0;"; + assert.strictEqual(called, 1); + style.cssText = "opacity: 0;"; + assert.strictEqual(called, 2); + }); + + it("onchange callback should be called only once when multiple properties were added", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: (cssText) => { + called++; + assert.strictEqual(cssText, "width: 100px; height: 100px;"); + } + }); + style.cssText = "width: 100px;height:100px;"; + assert.strictEqual(called, 1); + }); + + it("onchange callback should not be called when property is set to the same value", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: () => { + called++; + } + }); + + style.setProperty("opacity", 0); + assert.strictEqual(called, 1); + style.setProperty("opacity", 0); + assert.strictEqual(called, 1); + }); + + it("onchange callback should not be called when removeProperty was called on non-existing property", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleProperties(window, { + context: node, + onChange: () => { + called++; + } + }); + style.removeProperty("opacity"); + assert.strictEqual(called, 0); + }); + + it("setting improper css to csstext should not throw", () => { + const style = new CSSStyleProperties(window); + style.cssText = "color: "; + assert.strictEqual(style.cssText, ""); + style.color = "black"; + style.cssText = "float: "; + assert.strictEqual(style.cssText, ""); + }); + + it("url parsing works with quotes", () => { + const style = new CSSStyleProperties(window); + style.backgroundImage = "url(http://some/url/here1.png)"; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); + style.backgroundImage = "url('http://some/url/here2.png')"; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); + style.backgroundImage = 'url("http://some/url/here3.png")'; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); + }); + + it("setting 0 to a padding or margin works", () => { + const style = new CSSStyleProperties(window); + style.padding = 0; + assert.strictEqual(style.cssText, "padding: 0px;"); + style.margin = "1em"; + style.marginTop = "0"; + assert.strictEqual(style.marginTop, "0px"); + }); + + it("setting ex units to a padding or margin works", () => { + const style = new CSSStyleProperties(window); + style.padding = "1ex"; + assert.strictEqual(style.cssText, "padding: 1ex;"); + style.margin = "1em"; + style.marginTop = "0.5ex"; + assert.strictEqual(style.marginTop, "0.5ex"); + }); + + it("setting empty string and null to a padding or margin works", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + function testParts(base, nullValue) { + const props = [base].concat(parts.map((part) => base + part)); + for (const prop of props) { + assert.strictEqual(style[prop], ""); + style[prop] = "10px"; + assert.strictEqual(style[prop], "10px"); + style[prop] = nullValue; + assert.strictEqual(style[prop], ""); + } + } + + testParts("margin", ""); + testParts("margin", null); + testParts("padding", ""); + testParts("padding", null); + }); + + it("setting undefined to a padding or margin does nothing", () => { + const style = new CSSStyleProperties(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + function testParts(base) { + const props = [base].concat(parts.map((part) => base + part)); + for (const prop of props) { + style[prop] = "10px"; + assert.strictEqual(style[prop], "10px"); + style[prop] = undefined; + assert.strictEqual(style[prop], "10px"); + } + } + + testParts("margin"); + testParts("padding"); + }); + + it("setting null to background works", () => { + const style = new CSSStyleProperties(window); + style.background = "red"; + assert.strictEqual(style.cssText, "background: red;"); + style.background = null; + assert.strictEqual(style.cssText, ""); + }); + + it("flex properties should keep their values", () => { + const style = new CSSStyleProperties(window); + style.flexDirection = "column"; + assert.strictEqual(style.cssText, "flex-direction: column;"); + style.flexDirection = "row"; + assert.strictEqual(style.cssText, "flex-direction: row;"); + }); + + it("camelcase properties are not assigned with `.setproperty()`", () => { + const style = new CSSStyleProperties(window); + style.setProperty("fontSize", "12px"); + assert.strictEqual(style.cssText, ""); + }); + + it("casing is ignored in `.setproperty()`", () => { + const style = new CSSStyleProperties(window); + style.setProperty("FoNt-SiZe", "12px"); + assert.strictEqual(style.fontSize, "12px"); + assert.strictEqual(style.getPropertyValue("font-size"), "12px"); + }); + + it("support non string entries in border-spacing", () => { + const style = new CSSStyleProperties(window); + style.borderSpacing = 0; + assert.strictEqual(style.cssText, "border-spacing: 0px;"); + }); + + it("float should be valid property for `.setproperty()`", () => { + const style = new CSSStyleProperties(window); + style.setProperty("float", "left"); + assert.strictEqual(style.float, "left"); + assert.strictEqual(style.getPropertyValue("float"), "left"); + }); + + it("flex-shrink works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex-shrink", 0); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); + style.setProperty("flex-shrink", 1); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.cssText, "flex-shrink: 1;"); + }); + + it("flex-grow works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex-grow", 2); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.cssText, "flex-grow: 2;"); + }); + + it("flex-basis works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex-basis", 0); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); + style.setProperty("flex-basis", "250px"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); + style.setProperty("flex-basis", "10em"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "10em"); + style.setProperty("flex-basis", "30%"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "30%"); + assert.strictEqual(style.cssText, "flex-basis: 30%;"); + }); + + it("shorthand flex works", () => { + const style = new CSSStyleProperties(window); + style.setProperty("flex", "none"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); + style.removeProperty("flex"); + style.removeProperty("flex-basis"); + style.setProperty("flex", "auto"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); + style.removeProperty("flex"); + style.setProperty("flex", "0 1 250px"); + assert.strictEqual(style.getPropertyValue("flex"), "0 1 250px"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); + style.removeProperty("flex"); + style.setProperty("flex", "2"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); + style.removeProperty("flex"); + style.setProperty("flex", "20%"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "20%"); + style.removeProperty("flex"); + style.setProperty("flex", "2 2"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "2"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); + style.removeProperty("flex"); + }); + + it("font-size get a valid value", () => { + const style = new CSSStyleProperties(window); + const invalidValue = "1r5px"; + style.cssText = "font-size: 15px"; + assert.strictEqual(1, style.length); + style.cssText = `font-size: ${invalidValue}`; + assert.strictEqual(0, style.length); + assert.strictEqual(undefined, style[0]); + }); + + it("getPropertyValue for custom properties in cssText", () => { + const style = new CSSStyleProperties(window); + style.cssText = "--foo: red"; + + assert.strictEqual(style.getPropertyValue("--foo"), "red"); + }); + + it("getPropertyValue for custom properties with setProperty", () => { + const style = new CSSStyleProperties(window); + style.setProperty("--bar", "blue"); + + assert.strictEqual(style.getPropertyValue("--bar"), "blue"); + }); + + it("getPropertyValue for custom properties with object setter", () => { + const style = new CSSStyleProperties(window); + style["--baz"] = "yellow"; + + assert.strictEqual(style.getPropertyValue("--baz"), ""); + }); + + it("custom properties are case-sensitive", () => { + const style = new CSSStyleProperties(window); + style.cssText = "--fOo: purple"; + + assert.strictEqual(style.getPropertyValue("--foo"), ""); + assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); + }); + + for (const property of [ + "width", + "height", + "margin", + "margin-top", + "bottom", + "right", + "padding" + ]) { + it(`supports calc for ${property}`, () => { + const style = new CSSStyleProperties(window); + style.setProperty(property, "calc(100% - 100px)"); + assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); + }); + } + + it("supports nested calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "calc(100% - calc(200px - 100px))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); + }); + + it("supports nested calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "calc(100% * calc(2 / 3))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); + }); + + it("supports var", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); + }); + + it("supports var with fallback", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo, 100px)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); + }); + + it("supports var with var fallback", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo, var(--bar))"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, var(--bar))"); + }); + + it("supports calc with var inside", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "calc(100% - var(--foo))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(100% - var(--foo))"); + }); + + it("supports var with calc inside", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "var(--foo, calc(var(--bar) + 3px))"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, calc(var(--bar) + 3px))"); + }); + + it("supports color var", () => { + const style = new CSSStyleProperties(window); + style.setProperty("color", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); + }); + + it("should not normalize if var() is included", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); + assert.strictEqual( + style.getPropertyValue("line-height"), + "calc( /* comment */ 100% - calc(var(--foo) *2 ))" + ); + }); + + it("supports abs", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "abs(1 - 2 * 3)"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(5)"); + }); + + it("supports abs inside calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "calc(abs(1) + abs(2))"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); + }); + + it("supports sign", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "sign(.1)"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); + }); + + it("supports sign inside calc", () => { + const style = new CSSStyleProperties(window); + style.setProperty("line-height", "calc(sign(.1) + sign(.2))"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(2)"); + }); + + it("no-op for setting undefined to width", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "10px"); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.setProperty("width", undefined); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.width = undefined; + assert.strictEqual(style.getPropertyValue("width"), "10px"); + }); + + it("shorthand serialization with shorthand and longhands mixed", () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: blue; background: red !important; background-color: green;"; + assert.strictEqual(style.cssText, "background: red !important;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(window); + style.cssText = + "border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;"; + assert.strictEqual(style.cssText, "border: 1px;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border-width: 1px;"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border: 1px; border-top: 1px !important;"; + assert.strictEqual( + style.cssText, + "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;" + ); + }); + + it("set cssText as none", () => { + const style = new CSSStyleProperties(window); + style.cssText = "border: none;"; + assert.strictEqual(style.cssText, "border: medium;"); + }); + + it("invalid cssText should be parsed", () => { + const style = new CSSStyleProperties(window); + style.cssText = "color: red; }"; + assert.strictEqual(style.cssText, "color: red;"); + }); + + it("single value flex with CSS-wide keyword", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial;"; + assert.strictEqual(style.flex, "initial"); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "initial"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex: initial;"); + }); + + it("single value flex with non-CSS-wide value", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: 0;"; + assert.strictEqual(style.flex, "0 1 0%"); + assert.strictEqual(style.flexGrow, "0"); + assert.strictEqual(style.flexShrink, "1"); + assert.strictEqual(style.flexBasis, "0%"); + assert.strictEqual(style.cssText, "flex: 0 1 0%;"); + }); + + it("multiple values flex with CSS-wide keyword", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial; flex-basis: initial; flex-shrink: initial;"; + assert.strictEqual(style.flex, "initial"); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "initial"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex: initial;"); + }); + + it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial; flex-shrink: 0;"; + assert.strictEqual(style.flex, ""); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "0"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;"); + }); + + it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { + const style = new CSSStyleProperties(window); + style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; + assert.strictEqual(style.flex, ""); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "2"); + assert.strictEqual(style.flexBasis, "0px"); + assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;"); + }); +}); + describe("background", () => { it("background-attachment should set / get keyword", () => { testPropertyValue("background-attachment", "fixed", "fixed"); @@ -2327,3 +3755,242 @@ describe("user interface", () => { testPropertyValue("outline-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); }); }); + +/* regression tests */ +describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { + it("should set global value unset", () => { + const style = new CSSStyleProperties(window); + style.setProperty("width", "10px"); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.setProperty("width", "unset"); + assert.strictEqual(style.getPropertyValue("width"), "unset"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { + it("should not set custom properties twice", () => { + const style = new CSSStyleProperties(window); + style.setProperty("--foo", 0); + style.setProperty("--foo", 1); + + assert.strictEqual(style.length, 1); + assert.strictEqual(style.item(0), "--foo"); + assert.strictEqual(style.item(1), ""); + assert.strictEqual(style.getPropertyValue("--foo"), "1"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { + it("should set stringified value", () => { + const style = new CSSStyleProperties(window); + style.setProperty("--foo", true); + assert.strictEqual(style.getPropertyValue("--foo"), "true"); + }); + + it("throws for setting Symbol", () => { + const style = new CSSStyleProperties(window); + assert.throws( + () => style.setProperty("width", Symbol("foo")), + (e) => { + assert.strictEqual(e instanceof TypeError, true); + assert.strictEqual(e.message, "Can not convert symbol to string."); + return true; + } + ); + assert.throws( + () => { + style.width = Symbol("foo"); + }, + (e) => { + assert.strictEqual(e instanceof TypeError, true); + assert.strictEqual(e.message, "Can not convert symbol to string."); + return true; + } + ); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { + it('returns empty string for "webkit-*", without leading "-"', () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: green; webkit-transform: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitTransform, ""); + }); + + it('should set/get value for "-webkit-*"', () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: green; -webkit-transform: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitTransform, "scale(3)"); + }); + + it('returns undefined for unknown "-webkit-*"', () => { + const style = new CSSStyleProperties(window); + style.cssText = "background-color: green; -webkit-foo: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitFoo, undefined); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { + it("no-op when setting undefined to border", () => { + const style = new CSSStyleProperties(window); + style.border = "1px solid green"; + assert.strictEqual(style.border, "1px solid green"); + style.border = undefined; + assert.strictEqual(style.border, "1px solid green"); + }); + + it("no-op when setting undefined to borderWidth", () => { + const style = new CSSStyleProperties(window); + style.borderWidth = "1px"; + assert.strictEqual(style.borderWidth, "1px"); + style.border = undefined; + assert.strictEqual(style.borderWidth, "1px"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () => { + it("should support keywords", () => { + const keywords = [ + "serif", + "sans-serif", + "cursive", + "fantasy", + "monospace", + "system-ui", + "math", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded" + ]; + const style = new CSSStyleProperties(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support generic() function keywords", () => { + const keywords = [ + "generic(fangsong)", + "generic(kai)", + "generic(khmer-mul)", + "generic(nastaliq)" + ]; + const style = new CSSStyleProperties(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + // see https://drafts.csswg.org/css-fonts-4/#changes-2021-12-21 + it("should support removed generic keywords as non generic family name", () => { + const keywords = ["emoji", "fangsong"]; + const style = new CSSStyleProperties(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support `-webkit-` prefixed family name", () => { + const style = new CSSStyleProperties(window); + style.fontFamily = "-webkit-body"; + assert.strictEqual(style.fontFamily, "-webkit-body"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { + it("should get normalized value for font shorthand", () => { + const style = new CSSStyleProperties(window); + style.font = "normal bold 4px sans-serif"; + assert.strictEqual(style.font, "bold 4px sans-serif"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { + it("should return value for each property", () => { + const style = new CSSStyleProperties(window); + const key = "background-color"; + const camel = "backgroundColor"; + const value = "var(--foo)"; + style[key] = value; + assert.strictEqual(style[key], value); + style[key] = null; + style[camel] = value; + assert.strictEqual(style[camel], value); + }); + + it("should set var() values for background-attachment correctly", () => { + const style = new CSSStyleProperties(window); + style.backgroundAttachment = "var(--foo)"; + assert.strictEqual(style.backgroundAttachment, "var(--foo)"); + style.setProperty("background-attachment", "var(--bar)"); + assert.strictEqual(style.backgroundAttachment, "var(--bar)"); + }); + + it("should allow changing a single property on a border, when border contains a css variable", () => { + const style = new CSSStyleProperties(window); + style.border = "0.1rem solid var(--my-color-value)"; + assert.strictEqual(style.border, "0.1rem solid var(--my-color-value)"); + style.borderWidth = "0.2rem"; + assert.strictEqual(style.borderWidth, "0.2rem"); + assert.strictEqual(style.border, ""); + }); + + it("should get value and priority", () => { + const style = new CSSStyleProperties(window); + style.cssText = "word-spacing: 1px !important;"; + assert.strictEqual(style.cssText, "word-spacing: 1px !important;", "cssText"); + assert.strictEqual(style.getPropertyValue("word-spacing"), "1px", "value"); + assert.strictEqual(style.getPropertyPriority("word-spacing"), "important", "priority"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3944", () => { + it("should get overwritten value", () => { + const style = new CSSStyleProperties(window); + style.cssText = + "background: linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center;"; + assert.strictEqual( + style.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(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.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center top;" + ); + assert.strictEqual(style.backgroundPosition, "center top"); + }); + + it("should not overwrite value", () => { + const style = new CSSStyleProperties(window); + style.cssText = + "background: linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center !important; background-position: top;"; + assert.strictEqual( + style.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center center !important;" + ); + assert.strictEqual(style.backgroundPosition, "center center"); + }); + + it("should get overwritten value", () => { + const style = new CSSStyleProperties(window); + style.cssText = + "background:linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center !important; background-position: top !important;"; + assert.strictEqual( + style.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center top !important;" + ); + assert.strictEqual(style.backgroundPosition, "center top"); + }); +}); diff --git a/test/camelize.test.js b/test/camelize.test.js index ce68f424..9d1cb011 100644 --- a/test/camelize.test.js +++ b/test/camelize.test.js @@ -2,7 +2,7 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); -const camelize = require("../lib/utils/camelize"); +const camelize = require("../scripts/camelize"); describe("dashedToCamelCase", () => { it("should not camelize custom property", () => { diff --git a/test/parsers.test.js b/test/parsers.test.js index 2964102a..b98ef630 100644 --- a/test/parsers.test.js +++ b/test/parsers.test.js @@ -267,6 +267,13 @@ describe("resolveCalc", () => { assert.strictEqual(output, "calc(10px + 100vh)"); }); + + it("should return serialized value", () => { + const input = "translate(calc(10%), 10%)"; + const output = parsers.resolveCalc(input); + + assert.strictEqual(output, "translate(calc(10%), 10%)"); + }); }); describe("parseNumber", () => { @@ -979,17 +986,17 @@ describe("parseAngle", () => { }); }); -describe("parseUrl", () => { +describe("parseURL", () => { it("should return undefined", () => { const input = ""; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); it("should return undefined", () => { const input = []; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); @@ -1001,7 +1008,7 @@ describe("parseUrl", () => { value: "foo" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, undefined); }); @@ -1013,7 +1020,7 @@ describe("parseUrl", () => { value: "" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("")'); }); @@ -1025,7 +1032,7 @@ describe("parseUrl", () => { value: "sample.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample.png")'); }); @@ -1037,7 +1044,7 @@ describe("parseUrl", () => { value: "sample\\\\-escaped.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample\\-escaped.png")'); }); @@ -1049,7 +1056,7 @@ describe("parseUrl", () => { value: "sample escaped -space.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample escaped -space.png")'); }); @@ -1061,7 +1068,7 @@ describe("parseUrl", () => { value: "sample\tescaped\t-tab.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample\tescaped\t-tab.png")'); }); @@ -1073,7 +1080,7 @@ describe("parseUrl", () => { value: "sample'escaped'-quote.png" } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); // prettier-ignore assert.strictEqual(output, "url(\"sample'escaped'-quote.png\")"); @@ -1086,7 +1093,7 @@ describe("parseUrl", () => { value: 'sample"escaped"-double-quote.png' } ]; - const output = parsers.parseUrl(input); + const output = parsers.parseURL(input); assert.strictEqual(output, 'url("sample\\"escaped\\"-double-quote.png")'); }); @@ -1389,8 +1396,10 @@ describe("parseCSS", () => { it("should get ast", () => { const input = "color: green !important;"; const opt = { - context: "declarationList", - parseValue: false + options: { + context: "declarationList", + parseValue: false + } }; const output = parsers.parseCSS(input, opt); assert.strictEqual(output.type, "DeclarationList"); @@ -1400,8 +1409,10 @@ describe("parseCSS", () => { it("should get ast", () => { const input = "green"; const opt = { - context: "value", - parseValue: false + options: { + context: "value", + parseValue: false + } }; const output = parsers.parseCSS(input, opt); assert.strictEqual(output.type, "Value"); @@ -1411,8 +1422,10 @@ describe("parseCSS", () => { it("should get object", () => { const input = "color: green !important;"; const opt = { - context: "declarationList", - parseValue: false + options: { + context: "declarationList", + parseValue: false + } }; const output = parsers.parseCSS(input, opt, true); const [ From 9b3f73c7e1e7d391005eff6b18f969b1749c3ba4 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Wed, 12 Nov 2025 07:40:57 +0900 Subject: [PATCH 02/29] Update index.js --- lib/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/index.js b/lib/index.js index 02b0527b..93b0da0b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -3,9 +3,11 @@ const { CSSStyleDeclaration } = require("./CSSStyleDeclaration"); const { CSSStyleProperties } = require("./CSSStyleProperties"); const propertyList = require("./generated/propertyList"); +const propertyNames = require("./generated/allProperties"); module.exports = { CSSStyleDeclaration, CSSStyleProperties, - propertyList: Object.fromEntries(propertyList) + propertyList: Object.fromEntries(propertyList), + propertyNames: [...propertyNames] }; From e59cb64c4654d258c7aab5b6b35a86138a5a2eb1 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 13 Nov 2025 10:17:53 +0900 Subject: [PATCH 03/29] Remove CSSStyleProperties --- lib/CSSStyleDeclaration.js | 17 + lib/CSSStyleProperties.js | 38 - lib/index.js | 2 - test/CSSStyleDeclaration.test.js | 3986 ++++++++++++++++++++++++++++- test/CSSStyleProperties.test.js | 3996 ------------------------------ 5 files changed, 3993 insertions(+), 4046 deletions(-) delete mode 100644 lib/CSSStyleProperties.js delete mode 100644 test/CSSStyleProperties.test.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 79f461f8..56745080 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -4,6 +4,7 @@ */ "use strict"; +const generatedProperties = require("./generated/properties"); const propertyList = require("./generated/propertyList"); const { borderProperties, @@ -20,6 +21,7 @@ const { parsePropertyValue, prepareValue } = require("./parsers"); +const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); const ELEMENT_NODE = 1; @@ -569,6 +571,21 @@ class CSSStyleDeclaration { } } +// Properties +Object.defineProperties(CSSStyleDeclaration.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(CSSStyleDeclaration.prototype, aliasProperty, declaration); + } + } +} + module.exports = { CSSStyleDeclaration }; diff --git a/lib/CSSStyleProperties.js b/lib/CSSStyleProperties.js deleted file mode 100644 index d693ccfe..00000000 --- a/lib/CSSStyleProperties.js +++ /dev/null @@ -1,38 +0,0 @@ -"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/index.js b/lib/index.js index 93b0da0b..7c7f2fd3 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,13 +1,11 @@ "use strict"; const { CSSStyleDeclaration } = require("./CSSStyleDeclaration"); -const { CSSStyleProperties } = require("./CSSStyleProperties"); const propertyList = require("./generated/propertyList"); const propertyNames = require("./generated/allProperties"); module.exports = { CSSStyleDeclaration, - CSSStyleProperties, propertyList: Object.fromEntries(propertyList), propertyNames: [...propertyNames] }; diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index d0c82680..4914d005 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -3,14 +3,130 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); +const propertyList = require("../lib/generated/propertyList"); +const camelize = require("../scripts/camelize"); -describe("CSSStyleDeclaration", () => { - const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException, - TypeError: globalThis.TypeError - }; +const window = { + getComputedStyle: () => {}, + DOMException: globalThis.DOMException, + TypeError: globalThis.TypeError +}; + +function testPropertyValue(property, value, expected) { + const style = new CSSStyleDeclaration(window); + let res; + + style.setProperty(property, value); + res = style.getPropertyValue(property); + assert.strictEqual(res, expected, `setProperty("${property}", '${value}')`); + + style.setProperty(property, undefined); + res = style.getPropertyValue(property); + assert.strictEqual(res, expected, `setProperty("${property}", undefined)`); + + style.setProperty(property, null); + res = style.getPropertyValue(property); + assert.strictEqual(res, "", `setProperty("${property}", null)`); + + style[property] = value; + res = style[property]; + assert.strictEqual(res, expected, `set["${property}"] = '${value}'`); + + style[property] = undefined; + res = style[property]; + assert.strictEqual(res, expected, `set["${property}"] = undefined`); + + style[property] = null; + res = style[property]; + assert.strictEqual(res, "", `set["${property}"] = null`); +} + +function testImplicitPropertyValue(property, value, expected, sub) { + const style = new CSSStyleDeclaration(); + let res; + + style.setProperty(property, value); + res = style.getPropertyValue(property); + assert.strictEqual(res, expected, `setProperty("${property}", '${value}')`); + for (const [key, subExpected] of sub) { + res = style.getPropertyValue(key); + assert.strictEqual( + res, + subExpected, + `setProperty("${property}", '${value}') implicitly changes the value of ${key}` + ); + } + + style.setProperty(property, undefined); + res = style.getPropertyValue(property); + assert.strictEqual(res, expected, `setProperty("${property}", undefined)`); + for (const [key, subExpected] of sub) { + res = style.getPropertyValue(key); + assert.strictEqual( + res, + subExpected, + `setProperty("${property}", undefined) does not change the value of ${key}` + ); + } + + style.setProperty(property, null); + res = style.getPropertyValue(property); + assert.strictEqual(res, "", `setProperty("${property}", null)`); + for (const [key] of sub) { + res = style.getPropertyValue(key); + assert.strictEqual( + res, + "", + `setProperty("${property}", null) implicitly changes the value of ${key}` + ); + } + + for (const key of sub.keys()) { + style.setProperty(property, value); + style.setProperty(key, "var(--foo)"); + res = style.getPropertyValue(property); + assert.strictEqual( + res, + "", + `setProperty("${key}", "var(--foo)") implicitly changes the value of ${property}` + ); + style.setProperty(property, null); + } + + style[property] = value; + res = style[property]; + assert.strictEqual(res, expected, `set["${property}"] = '${value}'`); + for (const [key, subExpected] of sub) { + res = style.getPropertyValue(key); + assert.strictEqual( + res, + subExpected, + `set["${property}"] = '${value}' implicitly changes the value of ${key}` + ); + } + + style[property] = undefined; + res = style[property]; + assert.strictEqual(res, expected, `set["${property}"] = undefined`); + for (const [key, subExpected] of sub) { + res = style.getPropertyValue(key); + assert.strictEqual( + res, + subExpected, + `set["${property}"] = undefined does not change the value of ${key}` + ); + } + + style[property] = null; + res = style[property]; + assert.strictEqual(res, "", `set["${property}"] = null`); + for (const [key] of sub) { + res = style.getPropertyValue(key); + assert.strictEqual(res, "", `set["${property}"] = null implicitly changes the value of ${key}`); + } +} +describe("CSSStyleDeclaration", () => { it("has methods", () => { const style = new CSSStyleDeclaration(window); assert.strictEqual(typeof style.item, "function"); @@ -123,7 +239,6 @@ describe("CSSStyleDeclaration", () => { it("custom properties are case-sensitive", () => { const style = new CSSStyleDeclaration(window); style.cssText = "--fOo: purple"; - assert.strictEqual(style.getPropertyValue("--foo"), ""); assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); }); @@ -131,21 +246,18 @@ describe("CSSStyleDeclaration", () => { it("getPropertyValue for custom properties in cssText", () => { 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(window); style.setProperty("--bar", "blue"); - assert.strictEqual(style.getPropertyValue("--bar"), "blue"); }); it("getPropertyValue for custom properties with object setter", () => { const style = new CSSStyleDeclaration(window); style["--baz"] = "yellow"; - assert.strictEqual(style.getPropertyValue("--baz"), ""); }); @@ -201,3 +313,3857 @@ describe("CSSStyleDeclaration", () => { ); }); }); + +describe("properties", () => { + it("is instanceof CSSStyleDeclaration", () => { + const style = new CSSStyleDeclaration(window); + assert.strictEqual(style instanceof CSSStyleDeclaration, true); + }); + + it("all dashed properties are included in propertyList", () => { + const style = new CSSStyleDeclaration(window); + for (const i in style) { + if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { + assert.strictEqual(propertyList.has(i), true, i); + } + } + }); + + it("has camelCased property for dashed property", () => { + const style = new CSSStyleDeclaration(window); + for (const i in style) { + if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { + const camel = camelize.dashedToCamelCase(i); + assert.ok(style[camel] !== undefined, i); + } + } + }); + + // FIXME: https://github.com/jsdom/cssstyle/issues/210 + it.skip("all webkit prefixed properties are included in propertyList", () => { + const style = new CSSStyleDeclaration(window); + for (const i in style) { + if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { + assert.strictEqual(propertyList.has(i), true, i); + } + } + }); + + it("has camelCased property for webkit prefixed property", () => { + const style = new CSSStyleDeclaration(window); + for (const i in style) { + if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { + const camel = camelize.dashedToCamelCase(i); + assert.ok(style[camel] !== undefined, i); + } + } + }); + + it("has PascalCased property for webkit prefixed property", () => { + const style = new CSSStyleDeclaration(window); + for (const i in style) { + if (/^webkit[A-Z]/.test(i)) { + const pascal = i.replace(/^webkit/, "Webkit"); + assert.ok(style[pascal] !== undefined); + } + } + }); + + it("from style string", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; + assert.strictEqual(style.length, 4); + assert.strictEqual( + style.cssText, + "color: blue; background-color: red; width: 78%; height: 50vh;" + ); + assert.strictEqual(style.getPropertyValue("color"), "blue"); + assert.strictEqual(style.item(0), "color"); + assert.strictEqual(style[1], "background-color"); + assert.strictEqual(style.backgroundColor, "red"); + style.cssText = ""; + assert.strictEqual(style.cssText, ""); + assert.strictEqual(style.length, 0); + }); + + it("from properties", () => { + const style = new CSSStyleDeclaration(window); + style.color = "blue"; + assert.strictEqual(style.length, 1); + assert.strictEqual(style[0], "color"); + assert.strictEqual(style.cssText, "color: blue;"); + assert.strictEqual(style.item(0), "color"); + assert.strictEqual(style.color, "blue"); + style.backgroundColor = "red"; + assert.strictEqual(style.length, 2); + assert.strictEqual(style[0], "color"); + assert.strictEqual(style[1], "background-color"); + assert.strictEqual(style.cssText, "color: blue; background-color: red;"); + assert.strictEqual(style.backgroundColor, "red"); + style.removeProperty("color"); + assert.strictEqual(style[0], "background-color"); + }); + + it("ignores invalid properties", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleDeclaration(window, { + context: node + }); + style.cssText = "color: green; color: invalid!; background: blue;"; + // ignores invalid properties + assert.strictEqual(style.cssText, "color: green; background: blue;"); + }); + + it("keeps the last one of the same property", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleDeclaration(window, { + context: node + }); + // 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;"); + }); + + it("ignores the nested selector rule", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleDeclaration(window, { + context: node + }); + // 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;"); + }); + + it("ignores the property immediately after the nested rule", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleDeclaration(window, { + context: node + }); + // 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;"); + }); + + it("includes the the property immediately after an at-rule", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleDeclaration(window, { + context: node + }); + // 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;"); + }); + + it("ignores the first property found after the nested selector rule along with the at-rules", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + const style = new CSSStyleDeclaration(window, { + context: node + }); + // 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("shorthand properties", () => { + const style = new CSSStyleDeclaration(window); + style.background = "blue url(http://www.example.com/some_img.jpg)"; + assert.strictEqual(style.backgroundColor, "blue"); + assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); + assert.strictEqual(style.background, 'url("http://www.example.com/some_img.jpg") blue'); + style.border = "0 solid black"; + assert.strictEqual(style.borderWidth, "0px"); + assert.strictEqual(style.borderStyle, "solid"); + assert.strictEqual(style.borderColor, "black"); + assert.strictEqual(style.borderTopWidth, "0px"); + assert.strictEqual(style.borderLeftStyle, "solid"); + assert.strictEqual(style.borderBottomColor, "black"); + style.font = "12em monospace"; + assert.strictEqual(style.fontSize, "12em"); + assert.strictEqual(style.fontFamily, "monospace"); + }); + + it("width and height properties and null and empty strings", () => { + const style = new CSSStyleDeclaration(window); + style.height = 6; + assert.strictEqual(style.height, ""); + style.width = 0; + assert.strictEqual(style.width, "0px"); + style.height = "34%"; + assert.strictEqual(style.height, "34%"); + style.height = "100vh"; + assert.strictEqual(style.height, "100vh"); + style.height = "100vw"; + assert.strictEqual(style.height, "100vw"); + style.height = ""; + assert.strictEqual(style.length, 1); + assert.strictEqual(style.cssText, "width: 0px;"); + style.width = null; + assert.strictEqual(style.length, 0); + assert.strictEqual(style.cssText, ""); + }); + + it("implicit properties", () => { + const style = new CSSStyleDeclaration(window); + style.borderWidth = 0; + assert.strictEqual(style.border, ""); + assert.strictEqual(style.borderWidth, "0px"); + assert.strictEqual(style.borderTopWidth, "0px"); + assert.strictEqual(style.borderBottomWidth, "0px"); + assert.strictEqual(style.borderLeftWidth, "0px"); + assert.strictEqual(style.borderRightWidth, "0px"); + assert.strictEqual(style.cssText, "border-width: 0px;"); + }); + + it("top, left, right, bottom properties", () => { + const style = new CSSStyleDeclaration(window); + style.top = 0; + style.left = "0%"; + style.right = "5em"; + style.bottom = "12pt"; + assert.strictEqual(style.top, "0px"); + assert.strictEqual(style.left, "0%"); + assert.strictEqual(style.right, "5em"); + assert.strictEqual(style.bottom, "12pt"); + assert.strictEqual(style.length, 4); + assert.strictEqual(style.cssText, "top: 0px; left: 0%; right: 5em; bottom: 12pt;"); + }); + + it('top, left, right, bottom properties should accept "auto"', () => { + const style = new CSSStyleDeclaration(window); + style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; + assert.strictEqual(style.top, "auto"); + assert.strictEqual(style.right, "auto"); + assert.strictEqual(style.bottom, "auto"); + assert.strictEqual(style.left, "auto"); + }); + + it("clear and clip properties", () => { + const style = new CSSStyleDeclaration(window); + style.clear = "none"; + assert.strictEqual(style.clear, "none"); + style.clear = "lfet"; + assert.strictEqual(style.clear, "none"); + style.clear = "left"; + assert.strictEqual(style.clear, "left"); + style.clear = "right"; + assert.strictEqual(style.clear, "right"); + style.clear = "both"; + assert.strictEqual(style.clear, "both"); + style.clip = "elipse(5px, 10px)"; + assert.strictEqual(style.clip, ""); + assert.strictEqual(style.length, 1); + style.clip = "rect(0, 3Em, 2pt, 50%)"; + assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 50%)"); + assert.strictEqual(style.length, 2); + assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); + }); + + it("colors", () => { + const style = new CSSStyleDeclaration(window); + style.color = "rgba(0,0,0,0)"; + assert.strictEqual(style.color, "rgba(0, 0, 0, 0)"); + style.color = "rgba(5%, 10%, 20%, 0.4)"; + assert.strictEqual(style.color, "rgba(13, 26, 51, 0.4)"); + style.color = "rgb(33%, 34%, 33%)"; + assert.strictEqual(style.color, "rgb(84, 87, 84)"); + style.color = "rgba(300, 200, 100, 1.5)"; + assert.strictEqual(style.color, "rgb(255, 200, 100)"); + style.color = "hsla(0, 1%, 2%, 0.5)"; + assert.strictEqual(style.color, "rgba(5, 5, 5, 0.5)"); + style.color = "hsl(0, 1%, 2%)"; + assert.strictEqual(style.color, "rgb(5, 5, 5)"); + style.color = "rebeccapurple"; + assert.strictEqual(style.color, "rebeccapurple"); + style.color = "transparent"; + assert.strictEqual(style.color, "transparent"); + style.color = "currentcolor"; + assert.strictEqual(style.color, "currentcolor"); + style.color = "#ffffffff"; + assert.strictEqual(style.color, "rgb(255, 255, 255)"); + style.color = "#fffa"; + assert.strictEqual(style.color, "rgba(255, 255, 255, 0.667)"); + style.color = "#ffffff66"; + assert.strictEqual(style.color, "rgba(255, 255, 255, 0.4)"); + }); + + it("invalid hex color value", () => { + const style = new CSSStyleDeclaration(window); + style.color = "#1234567"; + assert.strictEqual(style.color, ""); + }); + + it("shorthand properties with embedded spaces", () => { + let style = new CSSStyleDeclaration(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 CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.borderWidth = "1px"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("setting implicit properties to an empty string should clear all dependent properties", () => { + const style = new CSSStyleDeclaration(window); + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderWidth = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { + const style = new CSSStyleDeclaration(window); + style.borderTopWidth = "2px"; + assert.strictEqual(style.cssText, "border-top-width: 2px;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + style.borderTop = "2px solid black"; + assert.strictEqual(style.cssText, "border-top: 2px solid black;"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + }); + + it("set border as none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border as none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border-style as none", () => { + const style = new CSSStyleDeclaration(window); + style.borderStyle = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual(style.cssText, "border-style: none;", "cssText"); + }); + + it("set border-top as none", () => { + const style = new CSSStyleDeclaration(window); + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual(style.cssText, "border-top: medium;", "cssText"); + }); + + it("set border as 1px and change border-style to none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "1px"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "1px", "border"); + assert.strictEqual(style.borderWidth, "1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "1px", "border-top"); + assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: 1px;", "cssText"); + }); + + it("set border as 1px and change border-style to none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "1px"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "1px", "border"); + assert.strictEqual(style.borderWidth, "1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "1px", "border-top"); + assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: 1px;", "cssText"); + }); + + it("set border as 1px and change border-top to none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "1px"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as 1px solid and change border-top to none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "1px solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none solid solid; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as none and change border-style to null", () => { + const style = new CSSStyleDeclaration(window); + style.border = "none"; + style.borderStyle = null; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as solid and change border-top to none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual( + style.cssText, + "border-width: medium; border-style: none solid solid; border-color: currentcolor; border-image: none;", + "cssText" + ); + }); + + it("set border as solid and change border-style to none", () => { + const style = new CSSStyleDeclaration(window); + style.border = "solid"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "medium", "border"); + assert.strictEqual(style.borderWidth, "medium", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "none", "border-image"); + assert.strictEqual(style.cssText, "border: medium;", "cssText"); + }); + + it("set border-style as solid and change border-top to none", () => { + const style = new CSSStyleDeclaration(window); + style.borderStyle = "solid"; + style.borderTop = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual( + style.cssText, + "border-style: none solid solid; border-top-width: medium; border-top-color: currentcolor;", + "cssText" + ); + }); + + it("set border-top as solid and change border-style to none", () => { + const style = new CSSStyleDeclaration(window); + style.borderTop = "solid"; + style.borderStyle = "none"; + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "none", "border-style"); + assert.strictEqual(style.borderTop, "medium", "border-top"); + assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + assert.strictEqual( + style.cssText, + "border-top-width: medium; border-top-color: currentcolor; border-style: none;", + "cssText" + ); + }); + + it("set border-style as solid and change border-top to null", () => { + const style = new CSSStyleDeclaration(window); + style.borderStyle = "solid"; + style.borderTop = null; + assert.strictEqual( + style.cssText, + "border-right-style: solid; border-bottom-style: solid; border-left-style: solid;", + "cssText" + ); + assert.strictEqual(style.border, "", "border"); + assert.strictEqual(style.borderWidth, "", "border-width"); + assert.strictEqual(style.borderStyle, "", "border-style"); + assert.strictEqual(style.borderTop, "", "border-top"); + assert.strictEqual(style.borderTopWidth, "", "border-top-width"); + assert.strictEqual(style.borderTopStyle, "", "border-top-style"); + assert.strictEqual(style.borderImage, "", "border-image"); + }); + + it("setting border values to none should change dependent values", () => { + const style = new CSSStyleDeclaration(window); + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.border = "none"; + assert.strictEqual(style.border, "medium"); + assert.strictEqual(style.borderTop, "medium"); + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "medium"); + assert.strictEqual(style.cssText, "border: medium;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderStyle = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + assert.strictEqual(style.cssText, "border-top-width: 1px; border-style: none;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderTop = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "medium"); + assert.strictEqual(style.cssText, "border-top: medium;"); + + style.border = null; + style.borderImage = null; + style.borderTopWidth = "1px"; + assert.strictEqual(style.cssText, "border-top-width: 1px;"); + style.borderTopStyle = "none"; + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + assert.strictEqual(style.cssText, "border-top-width: 1px; border-top-style: none;"); + + style.border = null; + style.borderImage = null; + style.border = "1px"; + assert.strictEqual(style.cssText, "border: 1px;"); + assert.strictEqual(style.border, "1px"); + assert.strictEqual(style.borderTopStyle, "none"); + assert.strictEqual(style.borderTopWidth, "1px"); + style.borderTop = "none"; + assert.strictEqual( + style.cssText, + "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;" + ); + }); + + it("setting border to green", () => { + const style = new CSSStyleDeclaration(window); + style.border = "green"; + assert.strictEqual(style.cssText, "border: green;"); + assert.strictEqual(style.border, "green"); + }); + + it("setting border to green", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.border = "initial"; + assert.strictEqual(style.cssText, "border: initial;"); + assert.strictEqual(style.border, "initial"); + assert.strictEqual(style.borderWidth, "initial"); + assert.strictEqual(style.borderStyle, "initial"); + assert.strictEqual(style.borderColor, "initial"); + assert.strictEqual(style.borderTop, "initial"); + assert.strictEqual(style.borderTopWidth, "initial"); + assert.strictEqual(style.borderTopStyle, "initial"); + assert.strictEqual(style.borderTopColor, "initial"); + assert.strictEqual(style.borderImage, "none"); + }); + + it("setting borderTop to initial should set top related properties initial", () => { + const style = new CSSStyleDeclaration(window); + style.borderTop = "initial"; + assert.strictEqual(style.cssText, "border-top: initial;"); + assert.strictEqual(style.border, ""); + assert.strictEqual(style.borderWidth, ""); + assert.strictEqual(style.borderStyle, ""); + assert.strictEqual(style.borderColor, ""); + assert.strictEqual(style.borderTop, "initial"); + assert.strictEqual(style.borderTopWidth, "initial"); + assert.strictEqual(style.borderTopStyle, "initial"); + assert.strictEqual(style.borderTopColor, "initial"); + assert.strictEqual(style.borderImage, ""); + }); + + it("setting border to 0 should be okay", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.borderColor = "var(--foo)"; + assert.strictEqual(style.cssText, "border-color: var(--foo);"); + }); + + it("setting borderColor to inherit should be okay", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.cssText = "border: 1px solid black;"; + assert.strictEqual(style.cssText, "border: 1px solid black;"); + assert.strictEqual(style.borderTop, "1px solid black"); + style.border = ""; + assert.strictEqual(style.cssText, ""); + style.setProperty("border", "1px solid black"); + assert.strictEqual(style.cssText, "border: 1px solid black;"); + }); + + it("setting opacity should work", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("opacity", 0.75); + assert.strictEqual(style.cssText, "opacity: 0.75;"); + style.opacity = "0.50"; + assert.strictEqual(style.cssText, "opacity: 0.5;"); + style.opacity = 1; + assert.strictEqual(style.cssText, "opacity: 1;"); + }); + + it("width and height of auto should work", () => { + let style = new CSSStyleDeclaration(window); + style.width = "auto"; + assert.strictEqual(style.cssText, "width: auto;"); + assert.strictEqual(style.width, "auto"); + style = new CSSStyleDeclaration(window); + style.height = "auto"; + assert.strictEqual(style.cssText, "height: auto;"); + assert.strictEqual(style.height, "auto"); + }); + + it("Shorthand serialization with just longhands", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;"; + assert.strictEqual(style.cssText, "margin: 10px;"); + assert.strictEqual(style.margin, "10px"); + + style.cssText = + "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;"; + assert.strictEqual( + style.cssText, + "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;" + ); + assert.strictEqual(style.margin, ""); + + style.cssText = + "margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;"; + assert.strictEqual(style.cssText, "margin: 10px !important;"); + assert.strictEqual(style.margin, "10px"); + }); + + it("padding and margin should set/clear shorthand properties", () => { + const style = new CSSStyleDeclaration(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const testParts = function (name, v, V) { + style[name] = v; + for (let i = 0; i < 4; i++) { + const part = name + parts[i]; + assert.strictEqual(style[part], V[i]); + } + + assert.strictEqual(style[name], v); + style[name] = ""; + }; + testParts("padding", "1px", ["1px", "1px", "1px", "1px"]); + testParts("padding", "1px 2%", ["1px", "2%", "1px", "2%"]); + testParts("padding", "1px 2px 3px", ["1px", "2px", "3px", "2px"]); + testParts("padding", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); + style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = "1px"; + testParts("padding", "", ["", "", "", ""]); + testParts("margin", "1px", ["1px", "1px", "1px", "1px"]); + testParts("margin", "1px auto", ["1px", "auto", "1px", "auto"]); + testParts("margin", "1px 2% 3px", ["1px", "2%", "3px", "2%"]); + testParts("margin", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); + style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = "1px"; + testParts("margin", "", ["", "", "", ""]); + }); + + it("padding and margin shorthands should set main properties", () => { + const style = new CSSStyleDeclaration(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const testParts = function (name, v, V) { + let expected; + for (let i = 0; i < 4; i++) { + style[name] = v; + style[name + parts[i]] = V; + expected = v.split(/ /); + expected[i] = V; + expected = expected.join(" "); + + assert.strictEqual(style[name], expected); + } + }; + testParts("padding", "1px 2px 3px 4px", "10px"); + testParts("margin", "1px 2px 3px 4px", "10px"); + testParts("margin", "1px 2px 3px 4px", "auto"); + }); + + it("setting individual padding and margin properties to an empty string should clear them", () => { + const style = new CSSStyleDeclaration(window); + + const properties = ["padding", "margin"]; + const parts = ["Top", "Right", "Bottom", "Left"]; + for (let i = 0; i < properties.length; i++) { + for (let j = 0; j < parts.length; j++) { + const property = properties[i] + parts[j]; + style[property] = "12px"; + assert.strictEqual(style[property], "12px"); + + style[property] = ""; + assert.strictEqual(style[property], ""); + } + } + }); + + it("removing and setting individual margin properties updates the combined property accordingly", () => { + const style = new CSSStyleDeclaration(window); + style.margin = "1px 2px 3px 4px"; + style.marginTop = ""; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginBottom, "3px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginBottom = ""; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginBottom = "5px"; + assert.strictEqual(style.margin, ""); + assert.strictEqual(style.marginRight, "2px"); + assert.strictEqual(style.marginBottom, "5px"); + assert.strictEqual(style.marginLeft, "4px"); + + style.marginTop = "6px"; + assert.strictEqual(style.cssText, "margin: 6px 2px 5px 4px;"); + }); + + for (const property of ["padding", "margin"]) { + it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { + const style = new CSSStyleDeclaration(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + const partValues = ["1px", "2px", "3px", "4px"]; + + for (let j = 0; j < parts.length; j++) { + const partToRemove = parts[j]; + style[property] = partValues.join(" "); + style[property + partToRemove] = ""; + + // Main property should have been removed + assert.strictEqual(style[property], ""); + + // Expect other parts to still be there + for (let k = 0; k < parts.length; k++) { + const propertyCss = `${property}-${parts[k].toLowerCase()}: ${partValues[k]};`; + if (k === j) { + assert.strictEqual(style[property + parts[k]], ""); + assert.strictEqual(style.cssText.includes(propertyCss), false); + } else { + assert.strictEqual(style[property + parts[k]], partValues[k]); + assert.strictEqual(style.cssText.includes(propertyCss), true); + } + } + } + }); + + it(`setting additional ${property} properties keeps important status of others`, () => { + const style = new CSSStyleDeclaration(window); + const importantProperty = `${property}-top: 3px !important;`; + style.cssText = importantProperty; + assert.strictEqual(style.cssText.includes(importantProperty), true); + + style[`${property}Right`] = "4px"; + style[`${property}Bottom`] = "5px"; + style[`${property}Left`] = "6px"; + assert.strictEqual(style.cssText.includes(importantProperty), true); + assert.strictEqual(style.cssText.includes(`${property}-right: 4px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-bottom: 5px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-left: 6px;`), true); + assert.strictEqual(style.cssText.includes("margin:"), false); + }); + + it(`setting individual ${property} keeps important status of others`, () => { + const style = new CSSStyleDeclaration(window); + style.cssText = `${property}: 3px !important;`; + style[`${property}Top`] = "4px"; + assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); + assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); + assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); + assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); + assert.strictEqual(style.cssText.includes("margin:"), false); + }); + } + + it("setting a value to 0 should return the string value", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("fill-opacity", 0); + assert.strictEqual(style.fillOpacity, "0"); + }); + + it("onchange callback should be called when the csstext changes", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleDeclaration(window, { + context: node, + onChange: (cssText) => { + called++; + assert.strictEqual(cssText, "opacity: 0;"); + } + }); + style.cssText = "opacity: 0;"; + assert.strictEqual(called, 1); + style.cssText = "opacity: 0;"; + assert.strictEqual(called, 2); + }); + + it("onchange callback should be called only once when multiple properties were added", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleDeclaration(window, { + context: node, + onChange: (cssText) => { + called++; + assert.strictEqual(cssText, "width: 100px; height: 100px;"); + } + }); + style.cssText = "width: 100px;height:100px;"; + assert.strictEqual(called, 1); + }); + + it("onchange callback should not be called when property is set to the same value", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleDeclaration(window, { + context: node, + onChange: () => { + called++; + } + }); + + style.setProperty("opacity", 0); + assert.strictEqual(called, 1); + style.setProperty("opacity", 0); + assert.strictEqual(called, 1); + }); + + it("onchange callback should not be called when removeProperty was called on non-existing property", () => { + const node = { + nodeType: 1, + style: {}, + ownerDocument: { + defaultView: window + } + }; + let called = 0; + const style = new CSSStyleDeclaration(window, { + context: node, + onChange: () => { + called++; + } + }); + style.removeProperty("opacity"); + assert.strictEqual(called, 0); + }); + + it("setting improper css to csstext should not throw", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "color: "; + assert.strictEqual(style.cssText, ""); + style.color = "black"; + style.cssText = "float: "; + assert.strictEqual(style.cssText, ""); + }); + + it("url parsing works with quotes", () => { + const style = new CSSStyleDeclaration(window); + style.backgroundImage = "url(http://some/url/here1.png)"; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); + style.backgroundImage = "url('http://some/url/here2.png')"; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); + style.backgroundImage = 'url("http://some/url/here3.png")'; + assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); + }); + + it("setting 0 to a padding or margin works", () => { + const style = new CSSStyleDeclaration(window); + style.padding = 0; + assert.strictEqual(style.cssText, "padding: 0px;"); + style.margin = "1em"; + style.marginTop = "0"; + assert.strictEqual(style.marginTop, "0px"); + }); + + it("setting ex units to a padding or margin works", () => { + const style = new CSSStyleDeclaration(window); + style.padding = "1ex"; + assert.strictEqual(style.cssText, "padding: 1ex;"); + style.margin = "1em"; + style.marginTop = "0.5ex"; + assert.strictEqual(style.marginTop, "0.5ex"); + }); + + it("setting empty string and null to a padding or margin works", () => { + const style = new CSSStyleDeclaration(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + function testParts(base, nullValue) { + const props = [base].concat(parts.map((part) => base + part)); + for (const prop of props) { + assert.strictEqual(style[prop], ""); + style[prop] = "10px"; + assert.strictEqual(style[prop], "10px"); + style[prop] = nullValue; + assert.strictEqual(style[prop], ""); + } + } + + testParts("margin", ""); + testParts("margin", null); + testParts("padding", ""); + testParts("padding", null); + }); + + it("setting undefined to a padding or margin does nothing", () => { + const style = new CSSStyleDeclaration(window); + const parts = ["Top", "Right", "Bottom", "Left"]; + function testParts(base) { + const props = [base].concat(parts.map((part) => base + part)); + for (const prop of props) { + style[prop] = "10px"; + assert.strictEqual(style[prop], "10px"); + style[prop] = undefined; + assert.strictEqual(style[prop], "10px"); + } + } + + testParts("margin"); + testParts("padding"); + }); + + it("setting null to background works", () => { + const style = new CSSStyleDeclaration(window); + style.background = "red"; + assert.strictEqual(style.cssText, "background: red;"); + style.background = null; + assert.strictEqual(style.cssText, ""); + }); + + it("flex properties should keep their values", () => { + const style = new CSSStyleDeclaration(window); + style.flexDirection = "column"; + assert.strictEqual(style.cssText, "flex-direction: column;"); + style.flexDirection = "row"; + assert.strictEqual(style.cssText, "flex-direction: row;"); + }); + + it("camelcase properties are not assigned with `.setproperty()`", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("fontSize", "12px"); + assert.strictEqual(style.cssText, ""); + }); + + it("casing is ignored in `.setproperty()`", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.borderSpacing = 0; + assert.strictEqual(style.cssText, "border-spacing: 0px;"); + }); + + it("float should be valid property for `.setproperty()`", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("float", "left"); + assert.strictEqual(style.float, "left"); + assert.strictEqual(style.getPropertyValue("float"), "left"); + }); + + it("flex-shrink works", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("flex-shrink", 0); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); + style.setProperty("flex-shrink", 1); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.cssText, "flex-shrink: 1;"); + }); + + it("flex-grow works", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.setProperty("flex-basis", 0); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); + style.setProperty("flex-basis", "250px"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); + style.setProperty("flex-basis", "10em"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "10em"); + style.setProperty("flex-basis", "30%"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "30%"); + assert.strictEqual(style.cssText, "flex-basis: 30%;"); + }); + + it("shorthand flex works", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("flex", "none"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); + style.removeProperty("flex"); + style.removeProperty("flex-basis"); + style.setProperty("flex", "auto"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); + style.removeProperty("flex"); + style.setProperty("flex", "0 1 250px"); + assert.strictEqual(style.getPropertyValue("flex"), "0 1 250px"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); + style.removeProperty("flex"); + style.setProperty("flex", "2"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); + style.removeProperty("flex"); + style.setProperty("flex", "20%"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "20%"); + style.removeProperty("flex"); + style.setProperty("flex", "2 2"); + assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); + assert.strictEqual(style.getPropertyValue("flex-shrink"), "2"); + assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); + style.removeProperty("flex"); + }); + + it("font-size get a valid value", () => { + const style = new CSSStyleDeclaration(window); + const invalidValue = "1r5px"; + style.cssText = "font-size: 15px"; + assert.strictEqual(1, style.length); + style.cssText = `font-size: ${invalidValue}`; + assert.strictEqual(0, style.length); + assert.strictEqual(undefined, style[0]); + }); + + it("getPropertyValue for custom properties in cssText", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "--foo: red"; + + assert.strictEqual(style.getPropertyValue("--foo"), "red"); + }); + + it("getPropertyValue for custom properties with setProperty", () => { + 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(window); + style["--baz"] = "yellow"; + + assert.strictEqual(style.getPropertyValue("--baz"), ""); + }); + + it("custom properties are case-sensitive", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "--fOo: purple"; + + assert.strictEqual(style.getPropertyValue("--foo"), ""); + assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); + }); + + for (const property of [ + "width", + "height", + "margin", + "margin-top", + "bottom", + "right", + "padding" + ]) { + it(`supports calc for ${property}`, () => { + const style = new CSSStyleDeclaration(window); + style.setProperty(property, "calc(100% - 100px)"); + assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); + }); + } + + it("supports nested calc", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("width", "calc(100% - calc(200px - 100px))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); + }); + + it("supports nested calc", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("width", "calc(100% * calc(2 / 3))"); + assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); + }); + + it("supports var", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("width", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); + }); + + it("supports var with fallback", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("width", "var(--foo, 100px)"); + assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); + }); + + it("supports var with var fallback", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(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 CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.setProperty("color", "var(--foo)"); + assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); + }); + + it("should not normalize if var() is included", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); + assert.strictEqual( + style.getPropertyValue("line-height"), + "calc( /* comment */ 100% - calc(var(--foo) *2 ))" + ); + }); + + it("supports abs", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.setProperty("line-height", "calc(abs(1) + abs(2))"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); + }); + + it("supports sign", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("line-height", "sign(.1)"); + assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); + }); + + it("supports sign inside calc", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.setProperty("width", "10px"); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.setProperty("width", undefined); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.width = undefined; + assert.strictEqual(style.getPropertyValue("width"), "10px"); + }); + + it("shorthand serialization with shorthand and longhands mixed", () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.cssText = "border-width: 1px;"; + assert.strictEqual(style.cssText, "border-width: 1px;"); + }); + + it("shorthand serialization", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "border: 1px; border-top: 1px !important;"; + assert.strictEqual( + style.cssText, + "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;" + ); + }); + + it("set cssText as none", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "border: none;"; + assert.strictEqual(style.cssText, "border: medium;"); + }); + + it("invalid cssText should be parsed", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "color: red; }"; + assert.strictEqual(style.cssText, "color: red;"); + }); + + it("single value flex with CSS-wide keyword", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "flex: initial;"; + assert.strictEqual(style.flex, "initial"); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "initial"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex: initial;"); + }); + + it("single value flex with non-CSS-wide value", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "flex: 0;"; + assert.strictEqual(style.flex, "0 1 0%"); + assert.strictEqual(style.flexGrow, "0"); + assert.strictEqual(style.flexShrink, "1"); + assert.strictEqual(style.flexBasis, "0%"); + assert.strictEqual(style.cssText, "flex: 0 1 0%;"); + }); + + it("multiple values flex with CSS-wide keyword", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "flex: initial; flex-basis: initial; flex-shrink: initial;"; + assert.strictEqual(style.flex, "initial"); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "initial"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex: initial;"); + }); + + it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "flex: initial; flex-shrink: 0;"; + assert.strictEqual(style.flex, ""); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "0"); + assert.strictEqual(style.flexBasis, "initial"); + assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;"); + }); + + it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; + assert.strictEqual(style.flex, ""); + assert.strictEqual(style.flexGrow, "initial"); + assert.strictEqual(style.flexShrink, "2"); + assert.strictEqual(style.flexBasis, "0px"); + assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;"); + }); +}); + +describe("background", () => { + it("background-attachment should set / get keyword", () => { + testPropertyValue("background-attachment", "fixed", "fixed"); + }); + + it("background-attachment should set / get multiple values", () => { + testPropertyValue("background-attachment", "fixed, scroll", "fixed, scroll"); + }); + + it("background-clip should set / get keyword", () => { + testPropertyValue("background-clip", "border-box", "border-box"); + }); + + it("background-clip should set / get multiple values", () => { + testPropertyValue("background-clip", "padding-box, content-box", "padding-box, content-box"); + }); + + it("background-color should set / get color", () => { + testPropertyValue("background-color", "green", "green"); + }); + + it("background-color should set / get color", () => { + testPropertyValue("background-color", "#008000", "rgb(0, 128, 0)"); + }); + + it("background-color should set / get color", () => { + testPropertyValue("background-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); + + it("background-color should not set / get multiple values", () => { + testPropertyValue("background-color", "green, blue", ""); + }); + + it("background-image should set / get keyword", () => { + testPropertyValue("background-image", "none", "none"); + }); + + it("background-image should set / get image URL", () => { + testPropertyValue("background-image", "url(example.png)", 'url("example.png")'); + }); + + it("background-image should set / get gradient image", () => { + testPropertyValue( + "background-image", + "linear-gradient(to right, green, blue)", + "linear-gradient(to right, green, blue)" + ); + }); + + it("background-image should set / get gradient image", () => { + testPropertyValue( + "background-image", + "radial-gradient(ellipse closest-side, #1e90ff 50%, #008000)", + "radial-gradient(closest-side, rgb(30, 144, 255) 50%, rgb(0, 128, 0))" + ); + }); + + it("background-image should set / get multiple values", () => { + testPropertyValue( + "background-image", + "url(example.png), linear-gradient(to right, green, blue)", + 'url("example.png"), linear-gradient(to right, green, blue)' + ); + }); + + it("background-origin should set / get keyword", () => { + testPropertyValue("background-origin", "border-box", "border-box"); + }); + + it("background-origin should set / get multiple values", () => { + testPropertyValue("background-origin", "padding-box, content-box", "padding-box, content-box"); + }); + + it("background-position should set / get keywords", () => { + testPropertyValue("background-position", "center", "center center"); + }); + + it("background-position should set / get keywords", () => { + testPropertyValue("background-position", "left", "left center"); + }); + + it("background-position should set / get keywords", () => { + testPropertyValue("background-position", "top", "center top"); + }); + + it("background-position should set / get keywords", () => { + testPropertyValue("background-position", "left center", "left center"); + }); + + it("background-position should set / get keywords", () => { + testPropertyValue("background-position", "top left", "left top"); + }); + + it("background-position should set / get keywords", () => { + testPropertyValue("background-position", "top center", "center top"); + }); + + it("background-position should not set / get keyword", () => { + testPropertyValue("background-position", "left right", ""); + }); + + it("background-position should set / get length keyword", () => { + testPropertyValue("background-position", "10px", "10px center"); + }); + + it("background-position should set / get length", () => { + testPropertyValue("background-position", "10px 20px", "10px 20px"); + }); + + it("background-position should set / get percent keyword", () => { + testPropertyValue("background-position", "10%", "10% center"); + }); + + it("background-position should set / get percents", () => { + testPropertyValue("background-position", "10% 20%", "10% 20%"); + }); + + it("background-position should set / get length percent", () => { + testPropertyValue("background-position", "10px 20%", "10px 20%"); + }); + + it("background-position should set / get keyword length", () => { + testPropertyValue("background-position", "left 10px", "left 10px"); + }); + + it("background-position should not set / get keyword length", () => { + testPropertyValue("background-position", "10px left", ""); + }); + + it("background-position should set / get keyword length", () => { + testPropertyValue("background-position", "10px top", "10px top"); + }); + + it("background-position should not set / get keyword length", () => { + testPropertyValue("background-position", "top 10px", ""); + }); + + it("background-position should not set / get keyword", () => { + testPropertyValue("background-position", "left right bottom", ""); + }); + + it("background-position should not set / get length percent", () => { + testPropertyValue("background-position", "10px 20% 30px", ""); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "center 10px center", ""); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "center center 10px", ""); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "left 10px top", "left 10px top"); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "left top 20px", "left top 20px"); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "top 20px left", "left top 20px"); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "top left 10px", "left 10px top"); + }); + + it("background-position should not set / get keyword", () => { + testPropertyValue("background-position", "left right top bottom", ""); + }); + + it("background-position should not set / get length percent", () => { + testPropertyValue("background-position", "10px 20% 30px 40%", ""); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "center 10px center 20px", ""); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "left 10px top 20px", "left 10px top 20px"); + }); + + it("background-position should set / get keyword length with offset", () => { + testPropertyValue("background-position", "top 10px left 20px", "left 20px top 10px"); + }); + + it("background-position should set / get multiple values", () => { + testPropertyValue("background-position", "left top, bottom right", "left top, right bottom"); + }); + + it("background-repeat should set / get keyword", () => { + testPropertyValue("background-repeat", "repeat", "repeat"); + }); + + it("background-repeat should set / get keyword keyword", () => { + testPropertyValue("background-repeat", "repeat no-repeat", "repeat-x"); + }); + + it("background-repeat should set / get keyword keyword", () => { + testPropertyValue("background-repeat", "no-repeat repeat", "repeat-y"); + }); + + it("background-repeat should set / get keyword keyword", () => { + testPropertyValue("background-repeat", "repeat repeat", "repeat"); + }); + + it("background-repeat should set / get keyword keyword", () => { + testPropertyValue("background-repeat", "repeat space", "repeat space"); + }); + + it("background-repeat should not set / get multiple axis keywords", () => { + testPropertyValue("background-repeat", "repeat-x repeat-y", ""); + }); + + it("background-repeat should set / get multiple values", () => { + testPropertyValue("background-repeat", "repeat, no-repeat", "repeat, no-repeat"); + }); + + it("background-size should set / get keyword", () => { + testPropertyValue("background-size", "contain", "contain"); + }); + + it("background-size should not set / get multiple ratio keywords", () => { + testPropertyValue("background-size", "contain cover", ""); + }); + + it("background-size should set / get keyword", () => { + testPropertyValue("background-size", "auto auto", "auto"); + }); + + it("background-size should set / get length and length", () => { + testPropertyValue("background-size", "10px 20px", "10px 20px"); + }); + + it("background-size should not set / get negative length", () => { + testPropertyValue("background-size", "-10px 20px", ""); + }); + + it("background-size should not set / get negative length", () => { + testPropertyValue("background-size", "10px -20px", ""); + }); + + it("background-size should set / get percent", () => { + testPropertyValue("background-size", "10% 20%", "10% 20%"); + }); + + it("background-size should not set / get negative percent", () => { + testPropertyValue("background-size", "-10% 20%", ""); + }); + + it("background-size should not set / get negative percent", () => { + testPropertyValue("background-size", "10% -20%%", ""); + }); + + it("background-size should set / get keyword and length", () => { + testPropertyValue("background-size", "auto 10px", "auto 10px"); + }); + + it("background-size should set / get length", () => { + testPropertyValue("background-size", "10px auto", "10px"); + }); + + it("background-size should set / get multiple values", () => { + testPropertyValue("background-size", "contain, cover", "contain, cover"); + }); + + it("background shorthand should set / get value", () => { + testImplicitPropertyValue( + "background", + "none", + "none", + new Map([ + ["background-image", "none"], + ["background-position", "0% 0%"], + ["background-size", "auto"], + ["background-repeat", "repeat"], + ["background-origin", "padding-box"], + ["background-clip", "border-box"], + ["background-attachment", "scroll"], + ["background-color", "transparent"] + ]) + ); + }); + + it("background shorthand should set / get value", () => { + testImplicitPropertyValue( + "background", + "transparent", + "transparent", + new Map([ + ["background-image", "none"], + ["background-position", "0% 0%"], + ["background-size", "auto"], + ["background-repeat", "repeat"], + ["background-origin", "padding-box"], + ["background-clip", "border-box"], + ["background-attachment", "scroll"], + ["background-color", "transparent"] + ]) + ); + }); + + it("background shorthand should set / get sub longhand values", () => { + testImplicitPropertyValue( + "background", + "fixed left / 50% repeat url(example.png) green", + 'url("example.png") left center / 50% fixed green', + new Map([ + ["background-image", 'url("example.png")'], + ["background-position", "left center"], + ["background-size", "50%"], + ["background-repeat", "repeat"], + ["background-origin", "padding-box"], + ["background-clip", "border-box"], + ["background-attachment", "fixed"], + ["background-color", "green"] + ]) + ); + }); + + it("background shorthand should set / get multiple values", () => { + testImplicitPropertyValue( + "background", + "fixed left / 50% repeat url(example.png), linear-gradient(to right, green, blue) green", + 'url("example.png") left center / 50% fixed, linear-gradient(to right, green, blue) green', + new Map([ + ["background-image", 'url("example.png"), linear-gradient(to right, green, blue)'], + ["background-position", "left center, 0% 0%"], + ["background-size", "50%, auto"], + ["background-repeat", "repeat, repeat"], + ["background-origin", "padding-box, padding-box"], + ["background-clip", "border-box, border-box"], + ["background-attachment", "fixed, scroll"], + ["background-color", "green"] + ]) + ); + }); + + it("background shorthand should set / get value", () => { + testImplicitPropertyValue( + "background", + "fixed left repeat url(example.png) var(--foo)", + "fixed left repeat url(example.png) var(--foo)", + new Map([ + ["background-image", ""], + ["background-position", ""], + ["background-size", ""], + ["background-repeat", ""], + ["background-origin", ""], + ["background-clip", ""], + ["background-attachment", ""], + ["background-color", ""] + ]) + ); + }); +}); + +describe("border", () => { + it("border-top-color should set / get color", () => { + testPropertyValue("border-top-color", "green", "green"); + }); + + it("border-top-color should set / get color", () => { + testPropertyValue("border-top-color", "#008000", "rgb(0, 128, 0)"); + }); + + it("border-top-color should set / get color", () => { + testPropertyValue("border-top-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); + + it("border-right-color should set / get color", () => { + testPropertyValue("border-right-color", "green", "green"); + }); + + it("border-right-color should set / get color", () => { + testPropertyValue("border-right-color", "#008000", "rgb(0, 128, 0)"); + }); + + it("border-right-color should set / get color", () => { + testPropertyValue("border-right-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); + + it("border-bottom-color should set / get color", () => { + testPropertyValue("border-bottom-color", "green", "green"); + }); + + it("border-bottom-color should set / get color", () => { + testPropertyValue("border-bottom-color", "#008000", "rgb(0, 128, 0)"); + }); + + it("border-bottom-color should set / get color", () => { + testPropertyValue("border-bottom-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); + + it("border-left-color should set / get color", () => { + testPropertyValue("border-left-color", "green", "green"); + }); + + it("border-left-color should set / get color", () => { + testPropertyValue("border-left-color", "#008000", "rgb(0, 128, 0)"); + }); + + it("border-left-color should set / get color", () => { + testPropertyValue("border-left-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); + + it("border-top-width should set / get keyword", () => { + testPropertyValue("border-top-width", "medium", "medium"); + }); + + it("border-top-width should set / get length", () => { + testPropertyValue("border-top-width", "10px", "10px"); + }); + + it("border-top-width should not set / get negative length", () => { + testPropertyValue("border-top-width", "-10px", ""); + }); + + it("border-right-width should set / get keyword", () => { + testPropertyValue("border-right-width", "medium", "medium"); + }); + + it("border-right-width should set / get length", () => { + testPropertyValue("border-right-width", "10px", "10px"); + }); + + it("border-right-width should not set / get negative length", () => { + testPropertyValue("border-right-width", "-10px", ""); + }); + + it("border-bottom-width should set / get keyword", () => { + testPropertyValue("border-bottom-width", "medium", "medium"); + }); + + it("border-bottom-width should set / get length", () => { + testPropertyValue("border-bottom-width", "10px", "10px"); + }); + + it("border-bottom-width should not set / get negative length", () => { + testPropertyValue("border-bottom-width", "-10px", ""); + }); + + it("border-left-width should set / get keyword", () => { + testPropertyValue("border-left-width", "medium", "medium"); + }); + + it("border-left-width should set / get length", () => { + testPropertyValue("border-left-width", "10px", "10px"); + }); + + it("border-left-width should not set / get negative length", () => { + testPropertyValue("border-left-width", "-10px", ""); + }); + + it("border-top-style should set / get keyword", () => { + testPropertyValue("border-top-style", "dotted", "dotted"); + }); + + it("border-right-style should set / get keyword", () => { + testPropertyValue("border-right-style", "dotted", "dotted"); + }); + + it("border-bottom-style should set / get keyword", () => { + testPropertyValue("border-bottom-style", "dotted", "dotted"); + }); + + it("border-left-style should set / get keyword", () => { + testPropertyValue("border-left-style", "dotted", "dotted"); + }); + + it("border-color should set / get color", () => { + testImplicitPropertyValue( + "border-color", + "green", + "green", + new Map([ + ["border-top-color", "green"], + ["border-right-color", "green"], + ["border-bottom-color", "green"], + ["border-left-color", "green"] + ]) + ); + }); + + it("border-color should set / get color", () => { + testImplicitPropertyValue( + "border-color", + "green blue", + "green blue", + new Map([ + ["border-top-color", "green"], + ["border-right-color", "blue"], + ["border-bottom-color", "green"], + ["border-left-color", "blue"] + ]) + ); + }); + + it("border-color should set / get color", () => { + testImplicitPropertyValue( + "border-color", + "green blue yellow", + "green blue yellow", + new Map([ + ["border-top-color", "green"], + ["border-right-color", "blue"], + ["border-bottom-color", "yellow"], + ["border-left-color", "blue"] + ]) + ); + }); + + it("border-color should set / get color", () => { + testImplicitPropertyValue( + "border-color", + "green blue yellow purple", + "green blue yellow purple", + new Map([ + ["border-top-color", "green"], + ["border-right-color", "blue"], + ["border-bottom-color", "yellow"], + ["border-left-color", "purple"] + ]) + ); + }); + + it("border-width should set / get keyword", () => { + testImplicitPropertyValue( + "border-width", + "thick", + "thick", + new Map([ + ["border-top-width", "thick"], + ["border-right-width", "thick"], + ["border-bottom-width", "thick"], + ["border-left-width", "thick"] + ]) + ); + }); + + it("border-width should set / get keyword", () => { + testImplicitPropertyValue( + "border-width", + "thick thin", + "thick thin", + new Map([ + ["border-top-width", "thick"], + ["border-right-width", "thin"], + ["border-bottom-width", "thick"], + ["border-left-width", "thin"] + ]) + ); + }); + + it("border-width should set / get keyword", () => { + testImplicitPropertyValue( + "border-width", + "thick thin medium", + "thick thin medium", + new Map([ + ["border-top-width", "thick"], + ["border-right-width", "thin"], + ["border-bottom-width", "medium"], + ["border-left-width", "thin"] + ]) + ); + }); + + it("border-width should set / get keyword and value", () => { + testImplicitPropertyValue( + "border-width", + "thick thin medium 10px", + "thick thin medium 10px", + new Map([ + ["border-top-width", "thick"], + ["border-right-width", "thin"], + ["border-bottom-width", "medium"], + ["border-left-width", "10px"] + ]) + ); + }); + + it("border-style should set / get keyword", () => { + testImplicitPropertyValue( + "border-style", + "dotted", + "dotted", + new Map([ + ["border-top-style", "dotted"], + ["border-right-style", "dotted"], + ["border-bottom-style", "dotted"], + ["border-left-style", "dotted"] + ]) + ); + }); + + it("border-style should set / get keyword", () => { + testImplicitPropertyValue( + "border-style", + "none", + "none", + new Map([ + ["border-top-style", "none"], + ["border-right-style", "none"], + ["border-bottom-style", "none"], + ["border-left-style", "none"] + ]) + ); + }); + + it("border-style should set / get keyword", () => { + testImplicitPropertyValue( + "border-style", + "dotted groove", + "dotted groove", + new Map([ + ["border-top-style", "dotted"], + ["border-right-style", "groove"], + ["border-bottom-style", "dotted"], + ["border-left-style", "groove"] + ]) + ); + }); + + it("border-style should set / get keyword", () => { + testImplicitPropertyValue( + "border-style", + "dotted groove double", + "dotted groove double", + new Map([ + ["border-top-style", "dotted"], + ["border-right-style", "groove"], + ["border-bottom-style", "double"], + ["border-left-style", "groove"] + ]) + ); + }); + + it("border-style should set / get keyword", () => { + testImplicitPropertyValue( + "border-style", + "dotted groove double none", + "dotted groove double none", + new Map([ + ["border-top-style", "dotted"], + ["border-right-style", "groove"], + ["border-bottom-style", "double"], + ["border-left-style", "none"] + ]) + ); + }); + + it("border-collapse should set / get keyword", () => { + testPropertyValue("border-collapse", "collapse", "collapse"); + }); + + it("border-spacing should set / get length", () => { + testPropertyValue("border-spacing", "10px", "10px"); + }); + + it("border-spacing should set / get length", () => { + testPropertyValue("border-spacing", "10px 20px", "10px 20px"); + }); + + it("border-top shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-top", + "1px solid green", + "1px solid green", + new Map([ + ["border-top-width", "1px"], + ["border-top-style", "solid"], + ["border-top-color", "green"] + ]) + ); + }); + + it("border-top shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-top", + "1px var(--foo) green", + "1px var(--foo) green", + new Map([ + ["border-top-width", ""], + ["border-top-style", ""], + ["border-top-color", ""] + ]) + ); + }); + + it("border-right shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-right", + "1px solid green", + "1px solid green", + new Map([ + ["border-right-width", "1px"], + ["border-right-style", "solid"], + ["border-right-color", "green"] + ]) + ); + }); + + it("border-right shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-right", + "1px var(--foo) green", + "1px var(--foo) green", + new Map([ + ["border-right-width", ""], + ["border-right-style", ""], + ["border-right-color", ""] + ]) + ); + }); + + it("border-bottom shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-bottom", + "1px solid green", + "1px solid green", + new Map([ + ["border-bottom-width", "1px"], + ["border-bottom-style", "solid"], + ["border-bottom-color", "green"] + ]) + ); + }); + + it("border-bottom shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-bottom", + "1px var(--foo) green", + "1px var(--foo) green", + new Map([ + ["border-bottom-width", ""], + ["border-bottom-style", ""], + ["border-bottom-color", ""] + ]) + ); + }); + + it("border-left shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-left", + "1px solid green", + "1px solid green", + new Map([ + ["border-left-width", "1px"], + ["border-left-style", "solid"], + ["border-left-color", "green"] + ]) + ); + }); + + it("border-left shorthand should set / get value", () => { + testImplicitPropertyValue( + "border-left", + "1px var(--foo) green", + "1px var(--foo) green", + new Map([ + ["border-left-width", ""], + ["border-left-style", ""], + ["border-left-color", ""] + ]) + ); + }); + + it("border shorthand should set / get value", () => { + testImplicitPropertyValue( + "border", + "1px solid green", + "1px solid green", + new Map([ + ["border-top-width", "1px"], + ["border-top-style", "solid"], + ["border-top-color", "green"], + ["border-right-width", "1px"], + ["border-right-style", "solid"], + ["border-right-color", "green"], + ["border-bottom-width", "1px"], + ["border-bottom-style", "solid"], + ["border-bottom-color", "green"], + ["border-left-width", "1px"], + ["border-left-style", "solid"], + ["border-left-color", "green"] + ]) + ); + }); + + it("border shorthand should set / get value", () => { + testImplicitPropertyValue( + "border", + "1px var(--foo) green", + "1px var(--foo) green", + new Map([ + ["border-top-width", ""], + ["border-top-style", ""], + ["border-top-color", ""], + ["border-right-width", ""], + ["border-right-style", ""], + ["border-right-color", ""], + ["border-bottom-width", ""], + ["border-bottom-style", ""], + ["border-bottom-color", ""], + ["border-left-width", ""], + ["border-left-style", ""], + ["border-left-color", ""] + ]) + ); + }); +}); + +describe("box model", () => { + it("margin-top should set / get keyword", () => { + testPropertyValue("margin-top", "auto", "auto"); + }); + + it("margin-top should set / get length", () => { + testPropertyValue("margin-top", "0", "0px"); + }); + + it("margin-top should set / get length", () => { + testPropertyValue("margin-top", "10px", "10px"); + }); + + it("margin-top should set / get negative length", () => { + testPropertyValue("margin-top", "-10px", "-10px"); + }); + + it("margin-top should set / get percent", () => { + testPropertyValue("margin-top", "10%", "10%"); + }); + + it("margin-top should set / get negative percent", () => { + testPropertyValue("margin-top", "-10%", "-10%"); + }); + + it("margin-right should set / get keyword", () => { + testPropertyValue("margin-right", "auto", "auto"); + }); + + it("margin-right should set / get length", () => { + testPropertyValue("margin-right", "0", "0px"); + }); + + it("margin-right should set / get length", () => { + testPropertyValue("margin-right", "10px", "10px"); + }); + + it("margin-right should set / get negative length", () => { + testPropertyValue("margin-right", "-10px", "-10px"); + }); + + it("margin-right should set / get percent", () => { + testPropertyValue("margin-right", "10%", "10%"); + }); + + it("margin-right should set / get negative percent", () => { + testPropertyValue("margin-right", "-10%", "-10%"); + }); + + it("margin-bottom should set / get keyword", () => { + testPropertyValue("margin-bottom", "auto", "auto"); + }); + + it("margin-bottom should set / get length", () => { + testPropertyValue("margin-bottom", "0", "0px"); + }); + + it("margin-bottom should set / get length", () => { + testPropertyValue("margin-bottom", "10px", "10px"); + }); + + it("margin-bottom should set / get negative length", () => { + testPropertyValue("margin-bottom", "-10px", "-10px"); + }); + + it("margin-bottom should set / get percent", () => { + testPropertyValue("margin-bottom", "10%", "10%"); + }); + + it("margin-bottom should set / get negative percent", () => { + testPropertyValue("margin-bottom", "-10%", "-10%"); + }); + + it("margin-left should set / get keyword", () => { + testPropertyValue("margin-left", "auto", "auto"); + }); + + it("margin-left should set / get length", () => { + testPropertyValue("margin-left", "0", "0px"); + }); + + it("margin-left should set / get length", () => { + testPropertyValue("margin-left", "10px", "10px"); + }); + + it("margin-left should set / get negative length", () => { + testPropertyValue("margin-left", "-10px", "-10px"); + }); + + it("margin-left should set / get percent", () => { + testPropertyValue("margin-left", "10%", "10%"); + }); + + it("margin-left should set / get negative percent", () => { + testPropertyValue("margin-left", "-10%", "-10%"); + }); + + it("margin shorthand should set / get keyword", () => { + testImplicitPropertyValue( + "margin", + "auto", + "auto", + new Map([ + ["margin-top", "auto"], + ["margin-right", "auto"], + ["margin-bottom", "auto"], + ["margin-left", "auto"] + ]) + ); + }); + + it("margin shorthand should set / get value", () => { + testImplicitPropertyValue( + "margin", + "10px", + "10px", + new Map([ + ["margin-top", "10px"], + ["margin-right", "10px"], + ["margin-bottom", "10px"], + ["margin-left", "10px"] + ]) + ); + }); + + it("margin shorthand should set / get value", () => { + testImplicitPropertyValue( + "margin", + "10px 20px", + "10px 20px", + new Map([ + ["margin-top", "10px"], + ["margin-right", "20px"], + ["margin-bottom", "10px"], + ["margin-left", "20px"] + ]) + ); + }); + + it("margin shorthand should set / get value", () => { + testImplicitPropertyValue( + "margin", + "10px 20px 30px", + "10px 20px 30px", + new Map([ + ["margin-top", "10px"], + ["margin-right", "20px"], + ["margin-bottom", "30px"], + ["margin-left", "20px"] + ]) + ); + }); + + it("margin shorthand should set / get value", () => { + testImplicitPropertyValue( + "margin", + "10px 20px 30px 40px", + "10px 20px 30px 40px", + new Map([ + ["margin-top", "10px"], + ["margin-right", "20px"], + ["margin-bottom", "30px"], + ["margin-left", "40px"] + ]) + ); + }); + + it("margin shorthand should set / get value", () => { + testImplicitPropertyValue( + "margin", + "10px var(--foo)", + "10px var(--foo)", + new Map([ + ["margin-top", ""], + ["margin-right", ""], + ["margin-bottom", ""], + ["margin-left", ""] + ]) + ); + }); + + it("margin shorthand should set / get value", () => { + testImplicitPropertyValue( + "margin", + "initial", + "initial", + new Map([ + ["margin-top", "initial"], + ["margin-right", "initial"], + ["margin-bottom", "initial"], + ["margin-left", "initial"] + ]) + ); + }); + + it("padding-top should set / get length", () => { + testPropertyValue("padding-top", "0", "0px"); + }); + + it("padding-top should set / get length", () => { + testPropertyValue("padding-top", "10px", "10px"); + }); + + it("padding-top should not set / get negative length", () => { + testPropertyValue("padding-top", "-10px", ""); + }); + + it("padding-top should set / get percent", () => { + testPropertyValue("padding-top", "10%", "10%"); + }); + + it("padding-top should not set / get negative percent", () => { + testPropertyValue("padding-top", "-10%", ""); + }); + + it("padding-right should set / get length", () => { + testPropertyValue("padding-right", "0", "0px"); + }); + + it("padding-right should set / get length", () => { + testPropertyValue("padding-right", "10px", "10px"); + }); + + it("padding-right should not set / get negative length", () => { + testPropertyValue("padding-right", "-10px", ""); + }); + + it("padding-right should set / get percent", () => { + testPropertyValue("padding-right", "10%", "10%"); + }); + + it("padding-right should not set / get negative percent", () => { + testPropertyValue("padding-right", "-10%", ""); + }); + + it("padding-bottom should set / get length", () => { + testPropertyValue("padding-bottom", "0", "0px"); + }); + + it("padding-bottom should set / get length", () => { + testPropertyValue("padding-bottom", "10px", "10px"); + }); + + it("padding-bottom should not set / get negative length", () => { + testPropertyValue("padding-bottom", "-10px", ""); + }); + + it("padding-bottom should set / get percent", () => { + testPropertyValue("padding-bottom", "10%", "10%"); + }); + + it("padding-bottom should not set / get negative percent", () => { + testPropertyValue("padding-bottom", "-10%", ""); + }); + + it("padding-left should set / get length", () => { + testPropertyValue("padding-left", "0", "0px"); + }); + + it("padding-left should set / get length", () => { + testPropertyValue("padding-left", "10px", "10px"); + }); + + it("padding-left should not set / get negative length", () => { + testPropertyValue("padding-left", "-10px", ""); + }); + + it("padding-left should set / get percent", () => { + testPropertyValue("padding-left", "10%", "10%"); + }); + + it("padding-left should not set / get negative percent", () => { + testPropertyValue("padding-left", "-10%", ""); + }); + + it("padding shorthand should set / get value", () => { + testImplicitPropertyValue( + "padding", + "10px", + "10px", + new Map([ + ["padding-top", "10px"], + ["padding-right", "10px"], + ["padding-bottom", "10px"], + ["padding-left", "10px"] + ]) + ); + }); + + it("padding shorthand should set / get value", () => { + testImplicitPropertyValue( + "padding", + "10px 20px", + "10px 20px", + new Map([ + ["padding-top", "10px"], + ["padding-right", "20px"], + ["padding-bottom", "10px"], + ["padding-left", "20px"] + ]) + ); + }); + + it("padding shorthand should set / get value", () => { + testImplicitPropertyValue( + "padding", + "10px 20px 30px", + "10px 20px 30px", + new Map([ + ["padding-top", "10px"], + ["padding-right", "20px"], + ["padding-bottom", "30px"], + ["padding-left", "20px"] + ]) + ); + }); + + it("padding shorthand should set / get value", () => { + testImplicitPropertyValue( + "padding", + "10px 20px 30px 40px", + "10px 20px 30px 40px", + new Map([ + ["padding-top", "10px"], + ["padding-right", "20px"], + ["padding-bottom", "30px"], + ["padding-left", "40px"] + ]) + ); + }); + + it("padding shorthand should set / get value", () => { + testImplicitPropertyValue( + "padding", + "10px var(--foo)", + "10px var(--foo)", + new Map([ + ["padding-top", ""], + ["padding-right", ""], + ["padding-bottom", ""], + ["padding-left", ""] + ]) + ); + }); + + it("padding shorthand should set / get value", () => { + testImplicitPropertyValue( + "padding", + "initial", + "initial", + new Map([ + ["padding-top", "initial"], + ["padding-right", "initial"], + ["padding-bottom", "initial"], + ["padding-left", "initial"] + ]) + ); + }); +}); + +describe("box sizing", () => { + it("height should set / get keyword", () => { + testPropertyValue("height", "auto", "auto"); + }); + + it("height should set / get length", () => { + testPropertyValue("height", "0", "0px"); + }); + + it("height should set / get length", () => { + testPropertyValue("height", "10px", "10px"); + }); + + it("height should not set / get negative length", () => { + testPropertyValue("height", "-10px", ""); + }); + + it("height should set / get percent", () => { + testPropertyValue("height", "10%", "10%"); + }); + + it("height should not set / get negative percent", () => { + testPropertyValue("height", "-10%", ""); + }); + + it("width should set / get keyword", () => { + testPropertyValue("width", "auto", "auto"); + }); + + it("height should set / get length", () => { + testPropertyValue("width", "0", "0px"); + }); + + it("width should set / get length", () => { + testPropertyValue("width", "10px", "10px"); + }); + + it("width should not set / get negative length", () => { + testPropertyValue("width", "-10px", ""); + }); + + it("width should set / get percent", () => { + testPropertyValue("width", "10%", "10%"); + }); + + it("width should not set / get negative percent", () => { + testPropertyValue("width", "-10%", ""); + }); +}); + +describe("color", () => { + it("color should set / get color name", () => { + testPropertyValue("color", "green", "green"); + }); + + it("color should set / get hex color", () => { + testPropertyValue("color", "#008000", "rgb(0, 128, 0)"); + }); + + it("color should set / get color function", () => { + testPropertyValue("color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); + + it("color should set / get color function", () => { + testPropertyValue( + "color", + "light-dark(#008000, #0000ff)", + "light-dark(rgb(0, 128, 0), rgb(0, 0, 255))" + ); + }); + + it("color should not should set / get invalid value", () => { + testPropertyValue( + "color", + "color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)", + "" + ); + }); + + it("color should not should set / get invalid value", () => { + testPropertyValue("color", "color(srgb 0 0 0 0)", ""); + }); + + it("opacity should set / get keyword", () => { + testPropertyValue("opacity", "inherit", "inherit"); + }); + + it("opacity should set / get number", () => { + testPropertyValue("opacity", "0.5", "0.5"); + }); + + it("opacity should set / get number", () => { + testPropertyValue("opacity", ".5", "0.5"); + }); + + it("opacity should set / get number", () => { + testPropertyValue("opacity", "1.5", "1.5"); + }); + + it("opacity should set / get number", () => { + testPropertyValue("opacity", "-1", "-1"); + }); + + it("opacity should set / get percent", () => { + testPropertyValue("opacity", "50%", "50%"); + }); + + it("opacity should set / get percent", () => { + testPropertyValue("opacity", "150%", "150%"); + }); + + it("opacity should set / get percent", () => { + testPropertyValue("opacity", "-50%", "-50%"); + }); +}); + +describe("display", () => { + it("display should set / get keyword", () => { + testPropertyValue("display", "block", "block"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline", "inline"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline-block", "inline-block"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flex", "flex"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline-flex", "inline-flex"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "grid", "grid"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline-grid", "inline-grid"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow", "block"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow-root", "flow-root"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "none", "none"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "contents", "contents"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "table-row", "table-row"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow list-item", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "list-item flow", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block list-item", "list-item"); + }); + + it("display should not set / get keyword", () => { + testPropertyValue("display", "list-item block", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow-root list-item", "flow-root list-item"); + }); + + it("display should not set / get keyword", () => { + testPropertyValue("display", "list-item flow-root", "flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block flow list-item", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block list-item flow", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block flow-root list-item", "flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block list-item flow-root", "flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow block list-item", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow list-item block", "list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow-root block list-item", "flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow-root list-item block", "flow-root list-item"); + }); + + it("display should not set / get keyword", () => { + testPropertyValue("display", "list-item block flow", "list-item"); + }); + + it("display should not set / get keyword", () => { + testPropertyValue("display", "list-item flow block", "list-item"); + }); + + it("display should not set / get keyword", () => { + testPropertyValue("display", "list-item block flow-root", "flow-root list-item"); + }); + + it("display should not set / get keyword", () => { + testPropertyValue("display", "list-item flow-root block", "flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline flow list-item", "inline list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline flow-root list-item", "inline flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "run-in flow list-item", "run-in list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "run-in flow-root list-item", "run-in flow-root list-item"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block flow", "block"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow block", "block"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block flow-root", "flow-root"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block flex", "flex"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "block ruby", "block ruby"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline flow", "inline"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow inline", "inline"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline flow-root", "inline-block"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline flex", "inline-flex"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "inline ruby", "ruby"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "run-in flow", "run-in"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "flow run-in", "run-in"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "run-in flow-root", "run-in flow-root"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "run-in flex", "run-in flex"); + }); + + it("display should set / get keyword", () => { + testPropertyValue("display", "run-in ruby", "run-in ruby"); + }); +}); + +describe("flex box", () => { + it("flex-grow should set / get number", () => { + testPropertyValue("flex-grow", ".5", "0.5"); + }); + + it("flex-grow should not set / get negative number", () => { + testPropertyValue("flex-grow", "-1", ""); + }); + + it("flex-shrink should set / get number", () => { + testPropertyValue("flex-shrink", ".5", "0.5"); + }); + + it("flex-shrink should not set / get negative number", () => { + testPropertyValue("flex-shrink", "-1", ""); + }); + + it("flex-basis should set / get keyword", () => { + testPropertyValue("flex-basis", "content", "content"); + }); + + it("flex-basis should not set / get length", () => { + testPropertyValue("flex-basis", "10px", "10px"); + }); + + it("flex-basis should not set / get length", () => { + testPropertyValue("flex-basis", "0", "0px"); + }); + + it("flex-basis should not set / get percent", () => { + testPropertyValue("flex-basis", "10%", "10%"); + }); + + it("flex-basis should not set / get percent", () => { + testPropertyValue("flex-basis", "0%", "0%"); + }); + + it("flex should set / get keyword", () => { + testPropertyValue("flex", "none", "0 0 auto"); + }); + + it("flex should set / get keyword", () => { + testPropertyValue("flex", "auto", "1 1 auto"); + }); + + it("flex should set / get keyword", () => { + testPropertyValue("flex", "initial", "initial"); + }); + + it("flex should set / get keyword", () => { + testPropertyValue("flex", "unset", "unset"); + }); + + it("flex shorthand should not set / get longhand value", () => { + testPropertyValue("flex", "2 1 3", ""); + }); + + it("flex shorthand should not set / get longhand value", () => { + testPropertyValue("flex", "2 1 calc(3)", ""); + }); + + it("flex shorthand should set / get longhand value", () => { + testPropertyValue("flex", "2", "2 1 0%"); + }); + + it("flex shorthand should set / get longhand value", () => { + testPropertyValue("flex", "10%", "1 1 10%"); + }); + + it("flex shorthand should set / get longhand value", () => { + testPropertyValue("flex", "2 10px", "2 1 10px"); + }); + + it("flex shorthand should set / get longhand value", () => { + testPropertyValue("flex", "2 3", "2 3 0%"); + }); + + it("flex shorthand should set / get longhand value", () => { + testPropertyValue("flex", "2 3 10px", "2 3 10px"); + }); + + it("flex shorthand should set / get longhand value", () => { + testImplicitPropertyValue( + "flex", + "2 var(--foo) 10px", + "2 var(--foo) 10px", + new Map([ + ["flex-grow", ""], + ["flex-shrink", ""], + ["flex-basis", ""] + ]) + ); + }); + + it("flex shorthand should set / get longhand value", () => { + testImplicitPropertyValue( + "flex", + "calc(2px * 3)", + "1 1 calc(6px)", + new Map([ + ["flex-grow", "1"], + ["flex-shrink", "1"], + ["flex-basis", "calc(6px)"] + ]) + ); + }); + + it("flex shorthand should set / get longhand value", () => { + testImplicitPropertyValue( + "flex", + "calc(2 * 3)", + "calc(6) 1 0%", + new Map([ + ["flex-grow", "calc(6)"], + ["flex-shrink", "1"], + ["flex-basis", "0%"] + ]) + ); + }); + + it("flex shorthand should set / get longhand value", () => { + testImplicitPropertyValue( + "flex", + "initial", + "initial", + new Map([ + ["flex-grow", "initial"], + ["flex-shrink", "initial"], + ["flex-basis", "initial"] + ]) + ); + }); +}); + +describe("font", () => { + it("font-style should set / get keyword", () => { + testPropertyValue("font-style", "italic", "italic"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "normal", "normal"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "none", "none"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "none", "none"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "common-ligatures", "common-ligatures"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "no-common-ligatures", "no-common-ligatures"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "small-caps", "small-caps"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "stylistic(flowing)", "stylistic(flowing)"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue( + "font-variant", + "stylistic(flowing) historical-forms styleset(flowing) character-variant(flowing) swash(flowing) ornaments(flowing) annotation(flowing)", + "stylistic(flowing) historical-forms styleset(flowing) character-variant(flowing) swash(flowing) ornaments(flowing) annotation(flowing)" + ); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "jis78", "jis78"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "ruby", "ruby"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "sub", "sub"); + }); + + it("font-variant should set / get keyword", () => { + testPropertyValue("font-variant", "super", "super"); + }); + + it("font-variant should not set / get invalid keywords", () => { + testPropertyValue("font-variant", "normal none", ""); + }); + + it("font-variant should not set / get invalid keywords", () => { + testPropertyValue("font-variant", "normal small-caps", ""); + }); + + it("font-variant should not set / get invalid keywords", () => { + testPropertyValue("font-variant", "none small-caps", ""); + }); + + it("font-weight should set / get keyword", () => { + testPropertyValue("font-weight", "bold", "bold"); + }); + + it("font-weight should set / get number", () => { + testPropertyValue("font-weight", "400", "400"); + }); + + it("font-weight should not set / get number greater than 1000", () => { + testPropertyValue("font-weight", "1001", ""); + }); + + it("font-weight should not set / get negative number", () => { + testPropertyValue("font-weight", "-1", ""); + }); + + it("font-size should set / get keyword", () => { + testPropertyValue("font-size", "medium", "medium"); + }); + + it("font-size should set / get keyword", () => { + testPropertyValue("font-size", "larger", "larger"); + }); + + it("font-size should set / get length", () => { + testPropertyValue("font-size", "1em", "1em"); + }); + + it("font-size not should set / get negative length", () => { + testPropertyValue("font-size", "-1em", ""); + }); + + it("font-size should set / get percent", () => { + testPropertyValue("font-size", "90%", "90%"); + }); + + it("font-size should not set / get negative percent", () => { + testPropertyValue("font-size", "-10%", ""); + }); + + it("line-height should set / get keyword", () => { + testPropertyValue("line-height", "normal", "normal"); + }); + + it("line-height should set / get number", () => { + testPropertyValue("line-height", "1.2", "1.2"); + }); + + it("line-height should not set / get negative number", () => { + testPropertyValue("line-height", "-1", ""); + }); + + it("line-height should set / get length", () => { + testPropertyValue("line-height", "10px", "10px"); + }); + + it("line-height should not set / get negative length", () => { + testPropertyValue("line-height", "-10px", ""); + }); + + it("line-height should set / get percent", () => { + testPropertyValue("line-height", "10%", "10%"); + }); + + it("line-height should not set / get negative percent", () => { + testPropertyValue("line-height", "-10%", ""); + }); + + it("font-family should set / get keyword", () => { + testPropertyValue("font-family", "sans-serif", "sans-serif"); + }); + + it("font-family should set / get keyword", () => { + testPropertyValue("font-family", '"sans-serif"', '"sans-serif"'); + }); + + it("font-family should set / get family name", () => { + testPropertyValue("font-family", "Times", "Times"); + }); + + it("font-family should set / get quoted family name", () => { + testPropertyValue("font-family", '"Times New Roman"', '"Times New Roman"'); + }); + + it("font-family should set / get family values", () => { + testPropertyValue( + "font-family", + 'Times, "Times New Roman", Georgia, serif', + 'Times, "Times New Roman", Georgia, serif' + ); + }); + + it("font-family should set / get family values", () => { + testPropertyValue("font-family", "Times\\ New Roman, serif", '"Times New Roman", serif'); + }); + + it("font-family should set / get family values", () => { + testPropertyValue("font-family", '"Times\\ New Roman", serif', '"Times New Roman", serif'); + }); + + it("font-family should set / get family values", () => { + testPropertyValue( + "font-family", + '"Gill Sans Extrabold", sans-serif', + '"Gill Sans Extrabold", sans-serif' + ); + }); + + it("font-family should set / get family values", () => { + testPropertyValue( + "font-family", + '"Goudy Bookletter 1911", sans-serif', + '"Goudy Bookletter 1911", sans-serif' + ); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "Goudy Bookletter 1911, sans-serif", ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "Red/Black, sans-serif", ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", '"Lucida" Grande, sans-serif', ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", 'Lucida "Grande", sans-serif', ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "Ahem!, sans-serif", ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "test@foo, sans-serif", ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "#POUND, sans-serif", ""); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "Hawaii 5-0, sans-serif", ""); + }); + + it("font-family should set / get family values", () => { + testPropertyValue("font-family", "generic(fangsong)", "generic(fangsong)"); + }); + + it("font-family should set / get family values", () => { + testPropertyValue("font-family", "generic(kai)", "generic(kai)"); + }); + + it("font-family should set / get family values", () => { + testPropertyValue("font-family", "generic(khmer-mul)", "generic(khmer-mul)"); + }); + + it("font-family should set / get family values", () => { + testPropertyValue("font-family", "generic(nastaliq)", "generic(nastaliq)"); + }); + + it("font-family should not set / get invalid family values", () => { + testPropertyValue("font-family", "generic(foo)", ""); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + 'normal medium Times, "Times New Roman", Georgia, serif', + 'medium Times, "Times New Roman", Georgia, serif', + new Map([ + ["font-style", "normal"], + ["font-variant", "normal"], + ["font-weight", "normal"], + ["font-size", "medium"], + ["line-height", "normal"], + ["font-family", 'Times, "Times New Roman", Georgia, serif'] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + "normal medium Gill Sans Extrabold, sans-serif", + 'medium "Gill Sans Extrabold", sans-serif', + new Map([ + ["font-style", "normal"], + ["font-variant", "normal"], + ["font-weight", "normal"], + ["font-size", "medium"], + ["line-height", "normal"], + ["font-family", '"Gill Sans Extrabold", sans-serif'] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + 'normal medium "Goudy Bookletter 1911", sans-serif', + 'medium "Goudy Bookletter 1911", sans-serif', + new Map([ + ["font-style", "normal"], + ["font-variant", "normal"], + ["font-weight", "normal"], + ["font-size", "medium"], + ["line-height", "normal"], + ["font-family", '"Goudy Bookletter 1911", sans-serif'] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + "normal medium generic(fangsong)", + "medium generic(fangsong)", + new Map([ + ["font-style", "normal"], + ["font-variant", "normal"], + ["font-weight", "normal"], + ["font-size", "medium"], + ["line-height", "normal"], + ["font-family", "generic(fangsong)"] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + "normal medium Goudy Bookletter 1911, sans-serif", + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + "normal medium Red/Black, sans-serif", + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + 'normal medium "Lucida" Grande, sans-serif', + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + 'normal medium Lucida "Grande", sans-serif', + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + "normal medium Ahem!, sans-serif", + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + "normal medium test@foo, sans-serif", + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + "normal medium #POUND, sans-serif", + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should not set / get invalid values", () => { + testImplicitPropertyValue( + "font", + "normal medium Hawaii 5-0, sans-serif", + "", + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + 'italic bold medium/1.2 Times, "Times New Roman", Georgia, serif', + 'italic bold medium / 1.2 Times, "Times New Roman", Georgia, serif', + new Map([ + ["font-style", "italic"], + ["font-variant", "normal"], + ["font-weight", "bold"], + ["font-size", "medium"], + ["line-height", "1.2"], + ["font-family", 'Times, "Times New Roman", Georgia, serif'] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + 'italic bold calc(3em/2)/1.2 Times, "Times New Roman", Georgia, serif', + 'italic bold calc(1.5em) / 1.2 Times, "Times New Roman", Georgia, serif', + new Map([ + ["font-style", "italic"], + ["font-variant", "normal"], + ["font-weight", "bold"], + ["font-size", "calc(1.5em)"], + ["line-height", "1.2"], + ["font-family", 'Times, "Times New Roman", Georgia, serif'] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + 'italic bold var(--foo)/1.2 Times, "Times New Roman", Georgia, serif', + 'italic bold var(--foo)/1.2 Times, "Times New Roman", Georgia, serif', + new Map([ + ["font-style", ""], + ["font-variant", ""], + ["font-weight", ""], + ["font-size", ""], + ["line-height", ""], + ["font-family", ""] + ]) + ); + }); + + it("font shorthand should set / get values", () => { + testImplicitPropertyValue( + "font", + "initial", + "initial", + new Map([ + ["font-style", "initial"], + ["font-variant", "initial"], + ["font-weight", "initial"], + ["font-size", "initial"], + ["line-height", "initial"], + ["font-family", "initial"] + ]) + ); + }); +}); + +describe("logical", () => { + it("clear should set / get keyword", () => { + testPropertyValue("clear", "left", "left"); + }); + + it("clear should set / get keyword", () => { + testPropertyValue("clear", "none", "none"); + }); + + it("float should set / get keyword", () => { + testPropertyValue("float", "left", "left"); + }); + + it("float should set / get keyword", () => { + testPropertyValue("float", "none", "none"); + }); +}); + +describe("masking", () => { + it("clip should set / get keyword", () => { + testPropertyValue("clip", "auto", "auto"); + }); + + it("clip should set / get legacy ", () => { + testPropertyValue("clip", "rect(0, 10px, 20%, 40EM)", "rect(0px, 10px, 20%, 40em)"); + }); +}); + +describe("positioning", () => { + it("bottom should set / get keyword", () => { + testPropertyValue("bottom", "auto", "auto"); + }); + + it("bottom should set / get length", () => { + testPropertyValue("bottom", "10px", "10px"); + }); + + it("bottom should set / get percent", () => { + testPropertyValue("bottom", "10%", "10%"); + }); + + it("left should set / get keyword", () => { + testPropertyValue("left", "auto", "auto"); + }); + + it("left should set / get length", () => { + testPropertyValue("left", "10px", "10px"); + }); + + it("left should set / get percent", () => { + testPropertyValue("left", "10%", "10%"); + }); + + it("right should set / get keyword", () => { + testPropertyValue("right", "auto", "auto"); + }); + + it("right should set / get length", () => { + testPropertyValue("right", "10px", "10px"); + }); + + it("right should set / get percent", () => { + testPropertyValue("right", "10%", "10%"); + }); + + it("top should set / get keyword", () => { + testPropertyValue("top", "auto", "auto"); + }); + + it("top should set / get length", () => { + testPropertyValue("top", "10px", "10px"); + }); + + it("top should set / get percent", () => { + testPropertyValue("top", "10%", "10%"); + }); +}); + +describe("user interface", () => { + it("outline-color should set / get color name", () => { + testPropertyValue("outline-color", "green", "green"); + }); + + it("outline-color should set / get hex color", () => { + testPropertyValue("outline-color", "#008000", "rgb(0, 128, 0)"); + }); + + it("outline-color should set / get color function", () => { + testPropertyValue("outline-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); + }); +}); + +/* regression tests */ +describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { + it("should set global value unset", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("width", "10px"); + assert.strictEqual(style.getPropertyValue("width"), "10px"); + + style.setProperty("width", "unset"); + assert.strictEqual(style.getPropertyValue("width"), "unset"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { + it("should not set custom properties twice", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", 0); + style.setProperty("--foo", 1); + + assert.strictEqual(style.length, 1); + assert.strictEqual(style.item(0), "--foo"); + assert.strictEqual(style.item(1), ""); + assert.strictEqual(style.getPropertyValue("--foo"), "1"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { + it("should set stringified value", () => { + const style = new CSSStyleDeclaration(window); + style.setProperty("--foo", true); + assert.strictEqual(style.getPropertyValue("--foo"), "true"); + }); + + it("throws for setting Symbol", () => { + const style = new CSSStyleDeclaration(window); + assert.throws( + () => style.setProperty("width", Symbol("foo")), + (e) => { + assert.strictEqual(e instanceof TypeError, true); + assert.strictEqual(e.message, "Can not convert symbol to string."); + return true; + } + ); + assert.throws( + () => { + style.width = Symbol("foo"); + }, + (e) => { + assert.strictEqual(e instanceof TypeError, true); + assert.strictEqual(e.message, "Can not convert symbol to string."); + return true; + } + ); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { + it('returns empty string for "webkit-*", without leading "-"', () => { + const style = new CSSStyleDeclaration(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 CSSStyleDeclaration(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 CSSStyleDeclaration(window); + style.cssText = "background-color: green; -webkit-foo: scale(3);"; + assert.strictEqual(style.backgroundColor, "green"); + assert.strictEqual(style.webkitFoo, undefined); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { + it("no-op when setting undefined to border", () => { + const style = new CSSStyleDeclaration(window); + style.border = "1px solid green"; + assert.strictEqual(style.border, "1px solid green"); + style.border = undefined; + assert.strictEqual(style.border, "1px solid green"); + }); + + it("no-op when setting undefined to borderWidth", () => { + const style = new CSSStyleDeclaration(window); + style.borderWidth = "1px"; + assert.strictEqual(style.borderWidth, "1px"); + style.border = undefined; + assert.strictEqual(style.borderWidth, "1px"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () => { + it("should support keywords", () => { + const keywords = [ + "serif", + "sans-serif", + "cursive", + "fantasy", + "monospace", + "system-ui", + "math", + "ui-serif", + "ui-sans-serif", + "ui-monospace", + "ui-rounded" + ]; + const style = new CSSStyleDeclaration(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support generic() function keywords", () => { + const keywords = [ + "generic(fangsong)", + "generic(kai)", + "generic(khmer-mul)", + "generic(nastaliq)" + ]; + const style = new CSSStyleDeclaration(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + // see https://drafts.csswg.org/css-fonts-4/#changes-2021-12-21 + it("should support removed generic keywords as non generic family name", () => { + const keywords = ["emoji", "fangsong"]; + const style = new CSSStyleDeclaration(window); + for (const keyword of keywords) { + style.fontFamily = keyword; + assert.strictEqual(style.fontFamily, keyword); + } + }); + + it("should support `-webkit-` prefixed family name", () => { + const style = new CSSStyleDeclaration(window); + style.fontFamily = "-webkit-body"; + assert.strictEqual(style.fontFamily, "-webkit-body"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { + it("should get normalized value for font shorthand", () => { + const style = new CSSStyleDeclaration(window); + style.font = "normal bold 4px sans-serif"; + assert.strictEqual(style.font, "bold 4px sans-serif"); + }); +}); + +describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { + it("should return value for each property", () => { + const style = new CSSStyleDeclaration(window); + const key = "background-color"; + const camel = "backgroundColor"; + const value = "var(--foo)"; + style[key] = value; + assert.strictEqual(style[key], value); + style[key] = null; + style[camel] = value; + assert.strictEqual(style[camel], value); + }); + + it("should set var() values for background-attachment correctly", () => { + const style = new CSSStyleDeclaration(window); + style.backgroundAttachment = "var(--foo)"; + assert.strictEqual(style.backgroundAttachment, "var(--foo)"); + style.setProperty("background-attachment", "var(--bar)"); + assert.strictEqual(style.backgroundAttachment, "var(--bar)"); + }); + + it("should allow changing a single property on a border, when border contains a css variable", () => { + const style = new CSSStyleDeclaration(window); + style.border = "0.1rem solid var(--my-color-value)"; + assert.strictEqual(style.border, "0.1rem solid var(--my-color-value)"); + style.borderWidth = "0.2rem"; + assert.strictEqual(style.borderWidth, "0.2rem"); + assert.strictEqual(style.border, ""); + }); + + it("should get value and priority", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = "word-spacing: 1px !important;"; + assert.strictEqual(style.cssText, "word-spacing: 1px !important;", "cssText"); + assert.strictEqual(style.getPropertyValue("word-spacing"), "1px", "value"); + assert.strictEqual(style.getPropertyPriority("word-spacing"), "important", "priority"); + }); +}); + +describe("regression test for https://github.com/jsdom/jsdom/issues/3944", () => { + it("should get overwritten value", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = + "background: linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center;"; + assert.strictEqual( + style.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(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.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center top;" + ); + assert.strictEqual(style.backgroundPosition, "center top"); + }); + + it("should not overwrite value", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = + "background: linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center !important; background-position: top;"; + assert.strictEqual( + style.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center center !important;" + ); + assert.strictEqual(style.backgroundPosition, "center center"); + }); + + it("should get overwritten value", () => { + const style = new CSSStyleDeclaration(window); + style.cssText = + "background:linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center !important; background-position: top !important;"; + assert.strictEqual( + style.cssText, + "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center top !important;" + ); + assert.strictEqual(style.backgroundPosition, "center top"); + }); +}); diff --git a/test/CSSStyleProperties.test.js b/test/CSSStyleProperties.test.js deleted file mode 100644 index dd626053..00000000 --- a/test/CSSStyleProperties.test.js +++ /dev/null @@ -1,3996 +0,0 @@ -"use strict"; - -const { describe, it } = require("node:test"); -const assert = require("node:assert/strict"); -const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); -const { CSSStyleProperties } = require("../lib/CSSStyleProperties"); -const propertyList = require("../lib/generated/propertyList"); -const camelize = require("../scripts/camelize"); - -const window = { - getComputedStyle: () => {}, - DOMException: globalThis.DOMException, - TypeError: globalThis.TypeError -}; - -function testPropertyValue(property, value, expected) { - const style = new CSSStyleProperties(window); - let res; - - style.setProperty(property, value); - res = style.getPropertyValue(property); - assert.strictEqual(res, expected, `setProperty("${property}", '${value}')`); - - style.setProperty(property, undefined); - res = style.getPropertyValue(property); - assert.strictEqual(res, expected, `setProperty("${property}", undefined)`); - - style.setProperty(property, null); - res = style.getPropertyValue(property); - assert.strictEqual(res, "", `setProperty("${property}", null)`); - - style[property] = value; - res = style[property]; - assert.strictEqual(res, expected, `set["${property}"] = '${value}'`); - - style[property] = undefined; - res = style[property]; - assert.strictEqual(res, expected, `set["${property}"] = undefined`); - - style[property] = null; - res = style[property]; - assert.strictEqual(res, "", `set["${property}"] = null`); -} - -function testImplicitPropertyValue(property, value, expected, sub) { - const style = new CSSStyleProperties(); - let res; - - style.setProperty(property, value); - res = style.getPropertyValue(property); - assert.strictEqual(res, expected, `setProperty("${property}", '${value}')`); - for (const [key, subExpected] of sub) { - res = style.getPropertyValue(key); - assert.strictEqual( - res, - subExpected, - `setProperty("${property}", '${value}') implicitly changes the value of ${key}` - ); - } - - style.setProperty(property, undefined); - res = style.getPropertyValue(property); - assert.strictEqual(res, expected, `setProperty("${property}", undefined)`); - for (const [key, subExpected] of sub) { - res = style.getPropertyValue(key); - assert.strictEqual( - res, - subExpected, - `setProperty("${property}", undefined) does not change the value of ${key}` - ); - } - - style.setProperty(property, null); - res = style.getPropertyValue(property); - assert.strictEqual(res, "", `setProperty("${property}", null)`); - for (const [key] of sub) { - res = style.getPropertyValue(key); - assert.strictEqual( - res, - "", - `setProperty("${property}", null) implicitly changes the value of ${key}` - ); - } - - for (const key of sub.keys()) { - style.setProperty(property, value); - style.setProperty(key, "var(--foo)"); - res = style.getPropertyValue(property); - assert.strictEqual( - res, - "", - `setProperty("${key}", "var(--foo)") implicitly changes the value of ${property}` - ); - style.setProperty(property, null); - } - - style[property] = value; - res = style[property]; - assert.strictEqual(res, expected, `set["${property}"] = '${value}'`); - for (const [key, subExpected] of sub) { - res = style.getPropertyValue(key); - assert.strictEqual( - res, - subExpected, - `set["${property}"] = '${value}' implicitly changes the value of ${key}` - ); - } - - style[property] = undefined; - res = style[property]; - assert.strictEqual(res, expected, `set["${property}"] = undefined`); - for (const [key, subExpected] of sub) { - res = style.getPropertyValue(key); - assert.strictEqual( - res, - subExpected, - `set["${property}"] = undefined does not change the value of ${key}` - ); - } - - style[property] = null; - res = style[property]; - assert.strictEqual(res, "", `set["${property}"] = null`); - for (const [key] of sub) { - res = style.getPropertyValue(key); - assert.strictEqual(res, "", `set["${property}"] = null implicitly changes the value of ${key}`); - } -} - -describe("CSSStyleProperties", () => { - it("is instanceof CSSStyleDeclaration", () => { - const style = new CSSStyleProperties(window); - assert.strictEqual(style instanceof CSSStyleDeclaration, true); - }); - - it("all dashed properties are included in propertyList", () => { - const style = new CSSStyleProperties(window); - for (const i in style) { - if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { - assert.strictEqual(propertyList.has(i), true, i); - } - } - }); - - it("has camelCased property for dashed property", () => { - const style = new CSSStyleProperties(window); - for (const i in style) { - if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { - const camel = camelize.dashedToCamelCase(i); - assert.ok(style[camel] !== undefined, i); - } - } - }); - - // FIXME: https://github.com/jsdom/cssstyle/issues/210 - it.skip("all webkit prefixed properties are included in propertyList", () => { - const style = new CSSStyleProperties(window); - for (const i in style) { - if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { - assert.strictEqual(propertyList.has(i), true, i); - } - } - }); - - it("has camelCased property for webkit prefixed property", () => { - const style = new CSSStyleProperties(window); - for (const i in style) { - if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { - const camel = camelize.dashedToCamelCase(i); - assert.ok(style[camel] !== undefined, i); - } - } - }); - - it("has PascalCased property for webkit prefixed property", () => { - const style = new CSSStyleProperties(window); - for (const i in style) { - if (/^webkit[A-Z]/.test(i)) { - const pascal = i.replace(/^webkit/, "Webkit"); - assert.ok(style[pascal] !== undefined); - } - } - }); - - it("setting cssFloat should also set float", () => { - const style = new CSSStyleProperties(window); - style.cssFloat = "left"; - assert.strictEqual(style.cssFloat, "left"); - assert.strictEqual(style.float, "left"); - }); - - it("setting float should also set cssfloat", () => { - const style = new CSSStyleProperties(window); - style.float = "left"; - assert.strictEqual(style.cssFloat, "left"); - assert.strictEqual(style.float, "left"); - }); - - it("from style string", () => { - const style = new CSSStyleProperties(window); - style.cssText = "color: blue; background-color: red; width: 78%; height: 50vh;"; - assert.strictEqual(style.length, 4); - assert.strictEqual( - style.cssText, - "color: blue; background-color: red; width: 78%; height: 50vh;" - ); - assert.strictEqual(style.getPropertyValue("color"), "blue"); - assert.strictEqual(style.item(0), "color"); - assert.strictEqual(style[1], "background-color"); - assert.strictEqual(style.backgroundColor, "red"); - style.cssText = ""; - assert.strictEqual(style.cssText, ""); - assert.strictEqual(style.length, 0); - }); - - it("from properties", () => { - const style = new CSSStyleProperties(window); - style.color = "blue"; - assert.strictEqual(style.length, 1); - assert.strictEqual(style[0], "color"); - assert.strictEqual(style.cssText, "color: blue;"); - assert.strictEqual(style.item(0), "color"); - assert.strictEqual(style.color, "blue"); - style.backgroundColor = "red"; - assert.strictEqual(style.length, 2); - assert.strictEqual(style[0], "color"); - assert.strictEqual(style[1], "background-color"); - assert.strictEqual(style.cssText, "color: blue; background-color: red;"); - assert.strictEqual(style.backgroundColor, "red"); - style.removeProperty("color"); - assert.strictEqual(style[0], "background-color"); - }); - - it("ignores invalid properties", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - const style = new CSSStyleProperties(window, { - context: node - }); - style.cssText = "color: green; color: invalid!; background: blue;"; - // ignores invalid properties - assert.strictEqual(style.cssText, "color: green; background: blue;"); - }); - - it("keeps the last one of the same property", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - const style = new CSSStyleProperties(window, { - context: node - }); - // 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;"); - }); - - it("ignores the nested selector rule", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - const style = new CSSStyleProperties(window, { - context: node - }); - // 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;"); - }); - - it("ignores the property immediately after the nested rule", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - const style = new CSSStyleProperties(window, { - context: node - }); - // 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;"); - }); - - it("includes the the property immediately after an at-rule", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - const style = new CSSStyleProperties(window, { - context: node - }); - // 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;"); - }); - - it("ignores the first property found after the nested selector rule along with the at-rules", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - const style = new CSSStyleProperties(window, { - context: node - }); - // 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("shorthand properties", () => { - const style = new CSSStyleProperties(window); - style.background = "blue url(http://www.example.com/some_img.jpg)"; - assert.strictEqual(style.backgroundColor, "blue"); - assert.strictEqual(style.backgroundImage, 'url("http://www.example.com/some_img.jpg")'); - assert.strictEqual(style.background, 'url("http://www.example.com/some_img.jpg") blue'); - style.border = "0 solid black"; - assert.strictEqual(style.borderWidth, "0px"); - assert.strictEqual(style.borderStyle, "solid"); - assert.strictEqual(style.borderColor, "black"); - assert.strictEqual(style.borderTopWidth, "0px"); - assert.strictEqual(style.borderLeftStyle, "solid"); - assert.strictEqual(style.borderBottomColor, "black"); - style.font = "12em monospace"; - assert.strictEqual(style.fontSize, "12em"); - assert.strictEqual(style.fontFamily, "monospace"); - }); - - it("width and height properties and null and empty strings", () => { - const style = new CSSStyleProperties(window); - style.height = 6; - assert.strictEqual(style.height, ""); - style.width = 0; - assert.strictEqual(style.width, "0px"); - style.height = "34%"; - assert.strictEqual(style.height, "34%"); - style.height = "100vh"; - assert.strictEqual(style.height, "100vh"); - style.height = "100vw"; - assert.strictEqual(style.height, "100vw"); - style.height = ""; - assert.strictEqual(style.length, 1); - assert.strictEqual(style.cssText, "width: 0px;"); - style.width = null; - assert.strictEqual(style.length, 0); - assert.strictEqual(style.cssText, ""); - }); - - it("implicit properties", () => { - const style = new CSSStyleProperties(window); - style.borderWidth = 0; - assert.strictEqual(style.border, ""); - assert.strictEqual(style.borderWidth, "0px"); - assert.strictEqual(style.borderTopWidth, "0px"); - assert.strictEqual(style.borderBottomWidth, "0px"); - assert.strictEqual(style.borderLeftWidth, "0px"); - assert.strictEqual(style.borderRightWidth, "0px"); - assert.strictEqual(style.cssText, "border-width: 0px;"); - }); - - it("top, left, right, bottom properties", () => { - const style = new CSSStyleProperties(window); - style.top = 0; - style.left = "0%"; - style.right = "5em"; - style.bottom = "12pt"; - assert.strictEqual(style.top, "0px"); - assert.strictEqual(style.left, "0%"); - assert.strictEqual(style.right, "5em"); - assert.strictEqual(style.bottom, "12pt"); - assert.strictEqual(style.length, 4); - assert.strictEqual(style.cssText, "top: 0px; left: 0%; right: 5em; bottom: 12pt;"); - }); - - it('top, left, right, bottom properties should accept "auto"', () => { - const style = new CSSStyleProperties(window); - style.cssText = `top: auto; right: auto; bottom: auto; left: auto;`; - assert.strictEqual(style.top, "auto"); - assert.strictEqual(style.right, "auto"); - assert.strictEqual(style.bottom, "auto"); - assert.strictEqual(style.left, "auto"); - }); - - it("clear and clip properties", () => { - const style = new CSSStyleProperties(window); - style.clear = "none"; - assert.strictEqual(style.clear, "none"); - style.clear = "lfet"; - assert.strictEqual(style.clear, "none"); - style.clear = "left"; - assert.strictEqual(style.clear, "left"); - style.clear = "right"; - assert.strictEqual(style.clear, "right"); - style.clear = "both"; - assert.strictEqual(style.clear, "both"); - style.clip = "elipse(5px, 10px)"; - assert.strictEqual(style.clip, ""); - assert.strictEqual(style.length, 1); - style.clip = "rect(0, 3Em, 2pt, 50%)"; - assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 50%)"); - assert.strictEqual(style.length, 2); - assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); - }); - - it("colors", () => { - const style = new CSSStyleProperties(window); - style.color = "rgba(0,0,0,0)"; - assert.strictEqual(style.color, "rgba(0, 0, 0, 0)"); - style.color = "rgba(5%, 10%, 20%, 0.4)"; - assert.strictEqual(style.color, "rgba(13, 26, 51, 0.4)"); - style.color = "rgb(33%, 34%, 33%)"; - assert.strictEqual(style.color, "rgb(84, 87, 84)"); - style.color = "rgba(300, 200, 100, 1.5)"; - assert.strictEqual(style.color, "rgb(255, 200, 100)"); - style.color = "hsla(0, 1%, 2%, 0.5)"; - assert.strictEqual(style.color, "rgba(5, 5, 5, 0.5)"); - style.color = "hsl(0, 1%, 2%)"; - assert.strictEqual(style.color, "rgb(5, 5, 5)"); - style.color = "rebeccapurple"; - assert.strictEqual(style.color, "rebeccapurple"); - style.color = "transparent"; - assert.strictEqual(style.color, "transparent"); - style.color = "currentcolor"; - assert.strictEqual(style.color, "currentcolor"); - style.color = "#ffffffff"; - assert.strictEqual(style.color, "rgb(255, 255, 255)"); - style.color = "#fffa"; - assert.strictEqual(style.color, "rgba(255, 255, 255, 0.667)"); - style.color = "#ffffff66"; - assert.strictEqual(style.color, "rgba(255, 255, 255, 0.4)"); - }); - - it("invalid hex color value", () => { - const style = new CSSStyleProperties(window); - style.color = "#1234567"; - assert.strictEqual(style.color, ""); - }); - - it("shorthand properties with embedded spaces", () => { - let style = new CSSStyleProperties(window); - style.background = "rgb(0, 0, 0) url(/something/somewhere.jpg)"; - assert.strictEqual(style.backgroundColor, "rgb(0, 0, 0)"); - assert.strictEqual(style.backgroundImage, 'url("/something/somewhere.jpg")'); - assert.strictEqual(style.cssText, 'background: url("/something/somewhere.jpg") rgb(0, 0, 0);'); - style = new CSSStyleProperties(window); - style.border = " 1px solid black "; - assert.strictEqual(style.border, "1px solid black"); - }); - - it("setting shorthand properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleProperties(window); - style.borderWidth = "1px"; - assert.strictEqual(style.cssText, "border-width: 1px;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("setting implicit properties to an empty string should clear all dependent properties", () => { - const style = new CSSStyleProperties(window); - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderWidth = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("setting a shorthand property, whose shorthands are implicit properties, to an empty string should clear all dependent properties", () => { - const style = new CSSStyleProperties(window); - style.borderTopWidth = "2px"; - assert.strictEqual(style.cssText, "border-top-width: 2px;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - style.borderTop = "2px solid black"; - assert.strictEqual(style.cssText, "border-top: 2px solid black;"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - }); - - it("set border as none", () => { - const style = new CSSStyleProperties(window); - style.border = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border as none", () => { - const style = new CSSStyleProperties(window); - style.border = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border-style as none", () => { - const style = new CSSStyleProperties(window); - style.borderStyle = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual(style.cssText, "border-style: none;", "cssText"); - }); - - it("set border-top as none", () => { - const style = new CSSStyleProperties(window); - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual(style.cssText, "border-top: medium;", "cssText"); - }); - - it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleProperties(window); - style.border = "1px"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "1px", "border"); - assert.strictEqual(style.borderWidth, "1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "1px", "border-top"); - assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: 1px;", "cssText"); - }); - - it("set border as 1px and change border-style to none", () => { - const style = new CSSStyleProperties(window); - style.border = "1px"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "1px", "border"); - assert.strictEqual(style.borderWidth, "1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "1px", "border-top"); - assert.strictEqual(style.borderTopWidth, "1px", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: 1px;", "cssText"); - }); - - it("set border as 1px and change border-top to none", () => { - const style = new CSSStyleProperties(window); - style.border = "1px"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as 1px solid and change border-top to none", () => { - const style = new CSSStyleProperties(window); - style.border = "1px solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium 1px 1px", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none solid solid; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as none and change border-style to null", () => { - const style = new CSSStyleProperties(window); - style.border = "none"; - style.borderStyle = null; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as solid and change border-top to none", () => { - const style = new CSSStyleProperties(window); - style.border = "solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual( - style.cssText, - "border-width: medium; border-style: none solid solid; border-color: currentcolor; border-image: none;", - "cssText" - ); - }); - - it("set border as solid and change border-style to none", () => { - const style = new CSSStyleProperties(window); - style.border = "solid"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "medium", "border"); - assert.strictEqual(style.borderWidth, "medium", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "none", "border-image"); - assert.strictEqual(style.cssText, "border: medium;", "cssText"); - }); - - it("set border-style as solid and change border-top to none", () => { - const style = new CSSStyleProperties(window); - style.borderStyle = "solid"; - style.borderTop = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none solid solid", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual( - style.cssText, - "border-style: none solid solid; border-top-width: medium; border-top-color: currentcolor;", - "cssText" - ); - }); - - it("set border-top as solid and change border-style to none", () => { - const style = new CSSStyleProperties(window); - style.borderTop = "solid"; - style.borderStyle = "none"; - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "none", "border-style"); - assert.strictEqual(style.borderTop, "medium", "border-top"); - assert.strictEqual(style.borderTopWidth, "medium", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "none", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - assert.strictEqual( - style.cssText, - "border-top-width: medium; border-top-color: currentcolor; border-style: none;", - "cssText" - ); - }); - - it("set border-style as solid and change border-top to null", () => { - const style = new CSSStyleProperties(window); - style.borderStyle = "solid"; - style.borderTop = null; - assert.strictEqual( - style.cssText, - "border-right-style: solid; border-bottom-style: solid; border-left-style: solid;", - "cssText" - ); - assert.strictEqual(style.border, "", "border"); - assert.strictEqual(style.borderWidth, "", "border-width"); - assert.strictEqual(style.borderStyle, "", "border-style"); - assert.strictEqual(style.borderTop, "", "border-top"); - assert.strictEqual(style.borderTopWidth, "", "border-top-width"); - assert.strictEqual(style.borderTopStyle, "", "border-top-style"); - assert.strictEqual(style.borderImage, "", "border-image"); - }); - - it("setting border values to none should change dependent values", () => { - const style = new CSSStyleProperties(window); - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.border = "none"; - assert.strictEqual(style.border, "medium"); - assert.strictEqual(style.borderTop, "medium"); - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "medium"); - assert.strictEqual(style.cssText, "border: medium;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderStyle = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - assert.strictEqual(style.cssText, "border-top-width: 1px; border-style: none;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderTop = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "medium"); - assert.strictEqual(style.cssText, "border-top: medium;"); - - style.border = null; - style.borderImage = null; - style.borderTopWidth = "1px"; - assert.strictEqual(style.cssText, "border-top-width: 1px;"); - style.borderTopStyle = "none"; - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - assert.strictEqual(style.cssText, "border-top-width: 1px; border-top-style: none;"); - - style.border = null; - style.borderImage = null; - style.border = "1px"; - assert.strictEqual(style.cssText, "border: 1px;"); - assert.strictEqual(style.border, "1px"); - assert.strictEqual(style.borderTopStyle, "none"); - assert.strictEqual(style.borderTopWidth, "1px"); - style.borderTop = "none"; - assert.strictEqual( - style.cssText, - "border-width: medium 1px 1px; border-style: none; border-color: currentcolor; border-image: none;" - ); - }); - - it("setting border to green", () => { - const style = new CSSStyleProperties(window); - style.border = "green"; - assert.strictEqual(style.cssText, "border: green;"); - assert.strictEqual(style.border, "green"); - }); - - it("setting border to green", () => { - const style = new CSSStyleProperties(window); - style.border = "green"; - assert.strictEqual(style.cssText, "border: green;"); - assert.strictEqual(style.border, "green"); - }); - - it("setting border to initial should set all properties initial", () => { - const style = new CSSStyleProperties(window); - style.border = "initial"; - assert.strictEqual(style.cssText, "border: initial;"); - assert.strictEqual(style.border, "initial"); - assert.strictEqual(style.borderWidth, "initial"); - assert.strictEqual(style.borderStyle, "initial"); - assert.strictEqual(style.borderColor, "initial"); - assert.strictEqual(style.borderTop, "initial"); - assert.strictEqual(style.borderTopWidth, "initial"); - assert.strictEqual(style.borderTopStyle, "initial"); - assert.strictEqual(style.borderTopColor, "initial"); - assert.strictEqual(style.borderImage, "none"); - }); - - it("setting borderTop to initial should set top related properties initial", () => { - const style = new CSSStyleProperties(window); - style.borderTop = "initial"; - assert.strictEqual(style.cssText, "border-top: initial;"); - assert.strictEqual(style.border, ""); - assert.strictEqual(style.borderWidth, ""); - assert.strictEqual(style.borderStyle, ""); - assert.strictEqual(style.borderColor, ""); - assert.strictEqual(style.borderTop, "initial"); - assert.strictEqual(style.borderTopWidth, "initial"); - assert.strictEqual(style.borderTopStyle, "initial"); - assert.strictEqual(style.borderTopColor, "initial"); - assert.strictEqual(style.borderImage, ""); - }); - - it("setting border to 0 should be okay", () => { - const style = new CSSStyleProperties(window); - style.border = 0; - assert.strictEqual(style.cssText, "border: 0px;"); - assert.strictEqual(style.border, "0px"); - }); - - it("setting borderColor to var() should be okay", () => { - const style = new CSSStyleProperties(window); - style.borderColor = "var(--foo)"; - assert.strictEqual(style.cssText, "border-color: var(--foo);"); - }); - - it("setting borderColor to inherit should be okay", () => { - const style = new CSSStyleProperties(window); - style.borderColor = "inherit"; - assert.strictEqual(style.cssText, "border-color: inherit;"); - }); - - it("setting values implicit and shorthand properties via csstext and setproperty should propagate to dependent properties", () => { - const style = new CSSStyleProperties(window); - style.cssText = "border: 1px solid black;"; - assert.strictEqual(style.cssText, "border: 1px solid black;"); - assert.strictEqual(style.borderTop, "1px solid black"); - style.border = ""; - assert.strictEqual(style.cssText, ""); - style.setProperty("border", "1px solid black"); - assert.strictEqual(style.cssText, "border: 1px solid black;"); - }); - - it("setting opacity should work", () => { - const style = new CSSStyleProperties(window); - style.setProperty("opacity", 0.75); - assert.strictEqual(style.cssText, "opacity: 0.75;"); - style.opacity = "0.50"; - assert.strictEqual(style.cssText, "opacity: 0.5;"); - style.opacity = 1; - assert.strictEqual(style.cssText, "opacity: 1;"); - }); - - it("width and height of auto should work", () => { - let style = new CSSStyleProperties(window); - style.width = "auto"; - assert.strictEqual(style.cssText, "width: auto;"); - assert.strictEqual(style.width, "auto"); - style = new CSSStyleProperties(window); - style.height = "auto"; - assert.strictEqual(style.cssText, "height: auto;"); - assert.strictEqual(style.height, "auto"); - }); - - it("Shorthand serialization with just longhands", () => { - const style = new CSSStyleProperties(window); - style.cssText = "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px;"; - assert.strictEqual(style.cssText, "margin: 10px;"); - assert.strictEqual(style.margin, "10px"); - - style.cssText = - "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px!important;"; - assert.strictEqual( - style.cssText, - "margin-right: 10px; margin-left: 10px; margin-top: 10px; margin-bottom: 10px !important;" - ); - assert.strictEqual(style.margin, ""); - - style.cssText = - "margin-right: 10px !important; margin-left: 10px !important; margin-top: 10px !important; margin-bottom: 10px!important;"; - assert.strictEqual(style.cssText, "margin: 10px !important;"); - assert.strictEqual(style.margin, "10px"); - }); - - it("padding and margin should set/clear shorthand properties", () => { - const style = new CSSStyleProperties(window); - const parts = ["Top", "Right", "Bottom", "Left"]; - const testParts = function (name, v, V) { - style[name] = v; - for (let i = 0; i < 4; i++) { - const part = name + parts[i]; - assert.strictEqual(style[part], V[i]); - } - - assert.strictEqual(style[name], v); - style[name] = ""; - }; - testParts("padding", "1px", ["1px", "1px", "1px", "1px"]); - testParts("padding", "1px 2%", ["1px", "2%", "1px", "2%"]); - testParts("padding", "1px 2px 3px", ["1px", "2px", "3px", "2px"]); - testParts("padding", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); - style.paddingTop = style.paddingRight = style.paddingBottom = style.paddingLeft = "1px"; - testParts("padding", "", ["", "", "", ""]); - testParts("margin", "1px", ["1px", "1px", "1px", "1px"]); - testParts("margin", "1px auto", ["1px", "auto", "1px", "auto"]); - testParts("margin", "1px 2% 3px", ["1px", "2%", "3px", "2%"]); - testParts("margin", "1px 2px 3px 4px", ["1px", "2px", "3px", "4px"]); - style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = "1px"; - testParts("margin", "", ["", "", "", ""]); - }); - - it("padding and margin shorthands should set main properties", () => { - const style = new CSSStyleProperties(window); - const parts = ["Top", "Right", "Bottom", "Left"]; - const testParts = function (name, v, V) { - let expected; - for (let i = 0; i < 4; i++) { - style[name] = v; - style[name + parts[i]] = V; - expected = v.split(/ /); - expected[i] = V; - expected = expected.join(" "); - - assert.strictEqual(style[name], expected); - } - }; - testParts("padding", "1px 2px 3px 4px", "10px"); - testParts("margin", "1px 2px 3px 4px", "10px"); - testParts("margin", "1px 2px 3px 4px", "auto"); - }); - - it("setting individual padding and margin properties to an empty string should clear them", () => { - const style = new CSSStyleProperties(window); - - const properties = ["padding", "margin"]; - const parts = ["Top", "Right", "Bottom", "Left"]; - for (let i = 0; i < properties.length; i++) { - for (let j = 0; j < parts.length; j++) { - const property = properties[i] + parts[j]; - style[property] = "12px"; - assert.strictEqual(style[property], "12px"); - - style[property] = ""; - assert.strictEqual(style[property], ""); - } - } - }); - - it("removing and setting individual margin properties updates the combined property accordingly", () => { - const style = new CSSStyleProperties(window); - style.margin = "1px 2px 3px 4px"; - style.marginTop = ""; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginBottom, "3px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginBottom = ""; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginBottom = "5px"; - assert.strictEqual(style.margin, ""); - assert.strictEqual(style.marginRight, "2px"); - assert.strictEqual(style.marginBottom, "5px"); - assert.strictEqual(style.marginLeft, "4px"); - - style.marginTop = "6px"; - assert.strictEqual(style.cssText, "margin: 6px 2px 5px 4px;"); - }); - - for (const property of ["padding", "margin"]) { - it(`removing an individual ${property} property should remove the combined property and replace it with the remaining individual ones`, () => { - const style = new CSSStyleProperties(window); - const parts = ["Top", "Right", "Bottom", "Left"]; - const partValues = ["1px", "2px", "3px", "4px"]; - - for (let j = 0; j < parts.length; j++) { - const partToRemove = parts[j]; - style[property] = partValues.join(" "); - style[property + partToRemove] = ""; - - // Main property should have been removed - assert.strictEqual(style[property], ""); - - // Expect other parts to still be there - for (let k = 0; k < parts.length; k++) { - const propertyCss = `${property}-${parts[k].toLowerCase()}: ${partValues[k]};`; - if (k === j) { - assert.strictEqual(style[property + parts[k]], ""); - assert.strictEqual(style.cssText.includes(propertyCss), false); - } else { - assert.strictEqual(style[property + parts[k]], partValues[k]); - assert.strictEqual(style.cssText.includes(propertyCss), true); - } - } - } - }); - - it(`setting additional ${property} properties keeps important status of others`, () => { - const style = new CSSStyleProperties(window); - const importantProperty = `${property}-top: 3px !important;`; - style.cssText = importantProperty; - assert.strictEqual(style.cssText.includes(importantProperty), true); - - style[`${property}Right`] = "4px"; - style[`${property}Bottom`] = "5px"; - style[`${property}Left`] = "6px"; - assert.strictEqual(style.cssText.includes(importantProperty), true); - assert.strictEqual(style.cssText.includes(`${property}-right: 4px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-bottom: 5px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-left: 6px;`), true); - assert.strictEqual(style.cssText.includes("margin:"), false); - }); - - it(`setting individual ${property} keeps important status of others`, () => { - const style = new CSSStyleProperties(window); - style.cssText = `${property}: 3px !important;`; - style[`${property}Top`] = "4px"; - assert.strictEqual(style.cssText.includes(`${property}-top: 4px;`), true); - assert.strictEqual(style.cssText.includes(`${property}-right: 3px !important;`), true); - assert.strictEqual(style.cssText.includes(`${property}-bottom: 3px !important;`), true); - assert.strictEqual(style.cssText.includes(`${property}-left: 3px !important;`), true); - assert.strictEqual(style.cssText.includes("margin:"), false); - }); - } - - it("setting a value to 0 should return the string value", () => { - const style = new CSSStyleProperties(window); - style.setProperty("fill-opacity", 0); - assert.strictEqual(style.fillOpacity, "0"); - }); - - it("onchange callback should be called when the csstext changes", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - let called = 0; - const style = new CSSStyleProperties(window, { - context: node, - onChange: (cssText) => { - called++; - assert.strictEqual(cssText, "opacity: 0;"); - } - }); - style.cssText = "opacity: 0;"; - assert.strictEqual(called, 1); - style.cssText = "opacity: 0;"; - assert.strictEqual(called, 2); - }); - - it("onchange callback should be called only once when multiple properties were added", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - let called = 0; - const style = new CSSStyleProperties(window, { - context: node, - onChange: (cssText) => { - called++; - assert.strictEqual(cssText, "width: 100px; height: 100px;"); - } - }); - style.cssText = "width: 100px;height:100px;"; - assert.strictEqual(called, 1); - }); - - it("onchange callback should not be called when property is set to the same value", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - let called = 0; - const style = new CSSStyleProperties(window, { - context: node, - onChange: () => { - called++; - } - }); - - style.setProperty("opacity", 0); - assert.strictEqual(called, 1); - style.setProperty("opacity", 0); - assert.strictEqual(called, 1); - }); - - it("onchange callback should not be called when removeProperty was called on non-existing property", () => { - const node = { - nodeType: 1, - style: {}, - ownerDocument: { - defaultView: window - } - }; - let called = 0; - const style = new CSSStyleProperties(window, { - context: node, - onChange: () => { - called++; - } - }); - style.removeProperty("opacity"); - assert.strictEqual(called, 0); - }); - - it("setting improper css to csstext should not throw", () => { - const style = new CSSStyleProperties(window); - style.cssText = "color: "; - assert.strictEqual(style.cssText, ""); - style.color = "black"; - style.cssText = "float: "; - assert.strictEqual(style.cssText, ""); - }); - - it("url parsing works with quotes", () => { - const style = new CSSStyleProperties(window); - style.backgroundImage = "url(http://some/url/here1.png)"; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here1.png")'); - style.backgroundImage = "url('http://some/url/here2.png')"; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here2.png")'); - style.backgroundImage = 'url("http://some/url/here3.png")'; - assert.strictEqual(style.backgroundImage, 'url("http://some/url/here3.png")'); - }); - - it("setting 0 to a padding or margin works", () => { - const style = new CSSStyleProperties(window); - style.padding = 0; - assert.strictEqual(style.cssText, "padding: 0px;"); - style.margin = "1em"; - style.marginTop = "0"; - assert.strictEqual(style.marginTop, "0px"); - }); - - it("setting ex units to a padding or margin works", () => { - const style = new CSSStyleProperties(window); - style.padding = "1ex"; - assert.strictEqual(style.cssText, "padding: 1ex;"); - style.margin = "1em"; - style.marginTop = "0.5ex"; - assert.strictEqual(style.marginTop, "0.5ex"); - }); - - it("setting empty string and null to a padding or margin works", () => { - const style = new CSSStyleProperties(window); - const parts = ["Top", "Right", "Bottom", "Left"]; - function testParts(base, nullValue) { - const props = [base].concat(parts.map((part) => base + part)); - for (const prop of props) { - assert.strictEqual(style[prop], ""); - style[prop] = "10px"; - assert.strictEqual(style[prop], "10px"); - style[prop] = nullValue; - assert.strictEqual(style[prop], ""); - } - } - - testParts("margin", ""); - testParts("margin", null); - testParts("padding", ""); - testParts("padding", null); - }); - - it("setting undefined to a padding or margin does nothing", () => { - const style = new CSSStyleProperties(window); - const parts = ["Top", "Right", "Bottom", "Left"]; - function testParts(base) { - const props = [base].concat(parts.map((part) => base + part)); - for (const prop of props) { - style[prop] = "10px"; - assert.strictEqual(style[prop], "10px"); - style[prop] = undefined; - assert.strictEqual(style[prop], "10px"); - } - } - - testParts("margin"); - testParts("padding"); - }); - - it("setting null to background works", () => { - const style = new CSSStyleProperties(window); - style.background = "red"; - assert.strictEqual(style.cssText, "background: red;"); - style.background = null; - assert.strictEqual(style.cssText, ""); - }); - - it("flex properties should keep their values", () => { - const style = new CSSStyleProperties(window); - style.flexDirection = "column"; - assert.strictEqual(style.cssText, "flex-direction: column;"); - style.flexDirection = "row"; - assert.strictEqual(style.cssText, "flex-direction: row;"); - }); - - it("camelcase properties are not assigned with `.setproperty()`", () => { - const style = new CSSStyleProperties(window); - style.setProperty("fontSize", "12px"); - assert.strictEqual(style.cssText, ""); - }); - - it("casing is ignored in `.setproperty()`", () => { - const style = new CSSStyleProperties(window); - style.setProperty("FoNt-SiZe", "12px"); - assert.strictEqual(style.fontSize, "12px"); - assert.strictEqual(style.getPropertyValue("font-size"), "12px"); - }); - - it("support non string entries in border-spacing", () => { - const style = new CSSStyleProperties(window); - style.borderSpacing = 0; - assert.strictEqual(style.cssText, "border-spacing: 0px;"); - }); - - it("float should be valid property for `.setproperty()`", () => { - const style = new CSSStyleProperties(window); - style.setProperty("float", "left"); - assert.strictEqual(style.float, "left"); - assert.strictEqual(style.getPropertyValue("float"), "left"); - }); - - it("flex-shrink works", () => { - const style = new CSSStyleProperties(window); - style.setProperty("flex-shrink", 0); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - style.setProperty("flex-shrink", 1); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.cssText, "flex-shrink: 1;"); - }); - - it("flex-grow works", () => { - const style = new CSSStyleProperties(window); - style.setProperty("flex-grow", 2); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.cssText, "flex-grow: 2;"); - }); - - it("flex-basis works", () => { - const style = new CSSStyleProperties(window); - style.setProperty("flex-basis", 0); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0px"); - style.setProperty("flex-basis", "250px"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); - style.setProperty("flex-basis", "10em"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "10em"); - style.setProperty("flex-basis", "30%"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "30%"); - assert.strictEqual(style.cssText, "flex-basis: 30%;"); - }); - - it("shorthand flex works", () => { - const style = new CSSStyleProperties(window); - style.setProperty("flex", "none"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "0"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.removeProperty("flex-basis"); - style.setProperty("flex", "auto"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "auto"); - style.removeProperty("flex"); - style.setProperty("flex", "0 1 250px"); - assert.strictEqual(style.getPropertyValue("flex"), "0 1 250px"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "0"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "250px"); - style.removeProperty("flex"); - style.setProperty("flex", "2"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); - style.removeProperty("flex"); - style.setProperty("flex", "20%"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "1"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "1"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "20%"); - style.removeProperty("flex"); - style.setProperty("flex", "2 2"); - assert.strictEqual(style.getPropertyValue("flex-grow"), "2"); - assert.strictEqual(style.getPropertyValue("flex-shrink"), "2"); - assert.strictEqual(style.getPropertyValue("flex-basis"), "0%"); - style.removeProperty("flex"); - }); - - it("font-size get a valid value", () => { - const style = new CSSStyleProperties(window); - const invalidValue = "1r5px"; - style.cssText = "font-size: 15px"; - assert.strictEqual(1, style.length); - style.cssText = `font-size: ${invalidValue}`; - assert.strictEqual(0, style.length); - assert.strictEqual(undefined, style[0]); - }); - - it("getPropertyValue for custom properties in cssText", () => { - const style = new CSSStyleProperties(window); - style.cssText = "--foo: red"; - - assert.strictEqual(style.getPropertyValue("--foo"), "red"); - }); - - it("getPropertyValue for custom properties with setProperty", () => { - const style = new CSSStyleProperties(window); - style.setProperty("--bar", "blue"); - - assert.strictEqual(style.getPropertyValue("--bar"), "blue"); - }); - - it("getPropertyValue for custom properties with object setter", () => { - const style = new CSSStyleProperties(window); - style["--baz"] = "yellow"; - - assert.strictEqual(style.getPropertyValue("--baz"), ""); - }); - - it("custom properties are case-sensitive", () => { - const style = new CSSStyleProperties(window); - style.cssText = "--fOo: purple"; - - assert.strictEqual(style.getPropertyValue("--foo"), ""); - assert.strictEqual(style.getPropertyValue("--fOo"), "purple"); - }); - - for (const property of [ - "width", - "height", - "margin", - "margin-top", - "bottom", - "right", - "padding" - ]) { - it(`supports calc for ${property}`, () => { - const style = new CSSStyleProperties(window); - style.setProperty(property, "calc(100% - 100px)"); - assert.strictEqual(style.getPropertyValue(property), "calc(100% - 100px)"); - }); - } - - it("supports nested calc", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "calc(100% - calc(200px - 100px))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(100% - 100px)"); - }); - - it("supports nested calc", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "calc(100% * calc(2 / 3))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(66.6667%)"); - }); - - it("supports var", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "var(--foo)"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo)"); - }); - - it("supports var with fallback", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "var(--foo, 100px)"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, 100px)"); - }); - - it("supports var with var fallback", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "var(--foo, var(--bar))"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, var(--bar))"); - }); - - it("supports calc with var inside", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "calc(100% - var(--foo))"); - assert.strictEqual(style.getPropertyValue("width"), "calc(100% - var(--foo))"); - }); - - it("supports var with calc inside", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "var(--foo, calc(var(--bar) + 3px))"); - assert.strictEqual(style.getPropertyValue("width"), "var(--foo, calc(var(--bar) + 3px))"); - }); - - it("supports color var", () => { - const style = new CSSStyleProperties(window); - style.setProperty("color", "var(--foo)"); - assert.strictEqual(style.getPropertyValue("color"), "var(--foo)"); - }); - - it("should not normalize if var() is included", () => { - const style = new CSSStyleProperties(window); - style.setProperty("line-height", "calc( /* comment */ 100% - calc(var(--foo) *2 ))"); - assert.strictEqual( - style.getPropertyValue("line-height"), - "calc( /* comment */ 100% - calc(var(--foo) *2 ))" - ); - }); - - it("supports abs", () => { - const style = new CSSStyleProperties(window); - style.setProperty("line-height", "abs(1 - 2 * 3)"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(5)"); - }); - - it("supports abs inside calc", () => { - const style = new CSSStyleProperties(window); - style.setProperty("line-height", "calc(abs(1) + abs(2))"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(3)"); - }); - - it("supports sign", () => { - const style = new CSSStyleProperties(window); - style.setProperty("line-height", "sign(.1)"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(1)"); - }); - - it("supports sign inside calc", () => { - const style = new CSSStyleProperties(window); - style.setProperty("line-height", "calc(sign(.1) + sign(.2))"); - assert.strictEqual(style.getPropertyValue("line-height"), "calc(2)"); - }); - - it("no-op for setting undefined to width", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "10px"); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.setProperty("width", undefined); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.width = undefined; - assert.strictEqual(style.getPropertyValue("width"), "10px"); - }); - - it("shorthand serialization with shorthand and longhands mixed", () => { - const style = new CSSStyleProperties(window); - style.cssText = "background-color: blue; background: red !important; background-color: green;"; - assert.strictEqual(style.cssText, "background: red !important;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleProperties(window); - style.cssText = - "border-top: 1px; border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none;"; - assert.strictEqual(style.cssText, "border: 1px;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleProperties(window); - style.cssText = "border-width: 1px;"; - assert.strictEqual(style.cssText, "border-width: 1px;"); - }); - - it("shorthand serialization", () => { - const style = new CSSStyleProperties(window); - style.cssText = "border: 1px; border-top: 1px !important;"; - assert.strictEqual( - style.cssText, - "border-right: 1px; border-bottom: 1px; border-left: 1px; border-image: none; border-top: 1px !important;" - ); - }); - - it("set cssText as none", () => { - const style = new CSSStyleProperties(window); - style.cssText = "border: none;"; - assert.strictEqual(style.cssText, "border: medium;"); - }); - - it("invalid cssText should be parsed", () => { - const style = new CSSStyleProperties(window); - style.cssText = "color: red; }"; - assert.strictEqual(style.cssText, "color: red;"); - }); - - it("single value flex with CSS-wide keyword", () => { - const style = new CSSStyleProperties(window); - style.cssText = "flex: initial;"; - assert.strictEqual(style.flex, "initial"); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "initial"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex: initial;"); - }); - - it("single value flex with non-CSS-wide value", () => { - const style = new CSSStyleProperties(window); - style.cssText = "flex: 0;"; - assert.strictEqual(style.flex, "0 1 0%"); - assert.strictEqual(style.flexGrow, "0"); - assert.strictEqual(style.flexShrink, "1"); - assert.strictEqual(style.flexBasis, "0%"); - assert.strictEqual(style.cssText, "flex: 0 1 0%;"); - }); - - it("multiple values flex with CSS-wide keyword", () => { - const style = new CSSStyleProperties(window); - style.cssText = "flex: initial; flex-basis: initial; flex-shrink: initial;"; - assert.strictEqual(style.flex, "initial"); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "initial"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex: initial;"); - }); - - it("multiple values flex with CSS-wide keywords and non-CSS-wide value", () => { - const style = new CSSStyleProperties(window); - style.cssText = "flex: initial; flex-shrink: 0;"; - assert.strictEqual(style.flex, ""); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "0"); - assert.strictEqual(style.flexBasis, "initial"); - assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: initial; flex-shrink: 0;"); - }); - - it("multiple values flex with CSS-wide and two non-CSS-wide-keyword values", () => { - const style = new CSSStyleProperties(window); - style.cssText = "flex: initial; flex-basis: 0; flex-shrink: 2;"; - assert.strictEqual(style.flex, ""); - assert.strictEqual(style.flexGrow, "initial"); - assert.strictEqual(style.flexShrink, "2"); - assert.strictEqual(style.flexBasis, "0px"); - assert.strictEqual(style.cssText, "flex-grow: initial; flex-basis: 0px; flex-shrink: 2;"); - }); -}); - -describe("background", () => { - it("background-attachment should set / get keyword", () => { - testPropertyValue("background-attachment", "fixed", "fixed"); - }); - - it("background-attachment should set / get multiple values", () => { - testPropertyValue("background-attachment", "fixed, scroll", "fixed, scroll"); - }); - - it("background-clip should set / get keyword", () => { - testPropertyValue("background-clip", "border-box", "border-box"); - }); - - it("background-clip should set / get multiple values", () => { - testPropertyValue("background-clip", "padding-box, content-box", "padding-box, content-box"); - }); - - it("background-color should set / get color", () => { - testPropertyValue("background-color", "green", "green"); - }); - - it("background-color should set / get color", () => { - testPropertyValue("background-color", "#008000", "rgb(0, 128, 0)"); - }); - - it("background-color should set / get color", () => { - testPropertyValue("background-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); - - it("background-color should not set / get multiple values", () => { - testPropertyValue("background-color", "green, blue", ""); - }); - - it("background-image should set / get keyword", () => { - testPropertyValue("background-image", "none", "none"); - }); - - it("background-image should set / get image URL", () => { - testPropertyValue("background-image", "url(example.png)", 'url("example.png")'); - }); - - it("background-image should set / get gradient image", () => { - testPropertyValue( - "background-image", - "linear-gradient(to right, green, blue)", - "linear-gradient(to right, green, blue)" - ); - }); - - it("background-image should set / get gradient image", () => { - testPropertyValue( - "background-image", - "radial-gradient(ellipse closest-side, #1e90ff 50%, #008000)", - "radial-gradient(closest-side, rgb(30, 144, 255) 50%, rgb(0, 128, 0))" - ); - }); - - it("background-image should set / get multiple values", () => { - testPropertyValue( - "background-image", - "url(example.png), linear-gradient(to right, green, blue)", - 'url("example.png"), linear-gradient(to right, green, blue)' - ); - }); - - it("background-origin should set / get keyword", () => { - testPropertyValue("background-origin", "border-box", "border-box"); - }); - - it("background-origin should set / get multiple values", () => { - testPropertyValue("background-origin", "padding-box, content-box", "padding-box, content-box"); - }); - - it("background-position should set / get keywords", () => { - testPropertyValue("background-position", "center", "center center"); - }); - - it("background-position should set / get keywords", () => { - testPropertyValue("background-position", "left", "left center"); - }); - - it("background-position should set / get keywords", () => { - testPropertyValue("background-position", "top", "center top"); - }); - - it("background-position should set / get keywords", () => { - testPropertyValue("background-position", "left center", "left center"); - }); - - it("background-position should set / get keywords", () => { - testPropertyValue("background-position", "top left", "left top"); - }); - - it("background-position should set / get keywords", () => { - testPropertyValue("background-position", "top center", "center top"); - }); - - it("background-position should not set / get keyword", () => { - testPropertyValue("background-position", "left right", ""); - }); - - it("background-position should set / get length keyword", () => { - testPropertyValue("background-position", "10px", "10px center"); - }); - - it("background-position should set / get length", () => { - testPropertyValue("background-position", "10px 20px", "10px 20px"); - }); - - it("background-position should set / get percent keyword", () => { - testPropertyValue("background-position", "10%", "10% center"); - }); - - it("background-position should set / get percents", () => { - testPropertyValue("background-position", "10% 20%", "10% 20%"); - }); - - it("background-position should set / get length percent", () => { - testPropertyValue("background-position", "10px 20%", "10px 20%"); - }); - - it("background-position should set / get keyword length", () => { - testPropertyValue("background-position", "left 10px", "left 10px"); - }); - - it("background-position should not set / get keyword length", () => { - testPropertyValue("background-position", "10px left", ""); - }); - - it("background-position should set / get keyword length", () => { - testPropertyValue("background-position", "10px top", "10px top"); - }); - - it("background-position should not set / get keyword length", () => { - testPropertyValue("background-position", "top 10px", ""); - }); - - it("background-position should not set / get keyword", () => { - testPropertyValue("background-position", "left right bottom", ""); - }); - - it("background-position should not set / get length percent", () => { - testPropertyValue("background-position", "10px 20% 30px", ""); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "center 10px center", ""); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "center center 10px", ""); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "left 10px top", "left 10px top"); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "left top 20px", "left top 20px"); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "top 20px left", "left top 20px"); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "top left 10px", "left 10px top"); - }); - - it("background-position should not set / get keyword", () => { - testPropertyValue("background-position", "left right top bottom", ""); - }); - - it("background-position should not set / get length percent", () => { - testPropertyValue("background-position", "10px 20% 30px 40%", ""); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "center 10px center 20px", ""); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "left 10px top 20px", "left 10px top 20px"); - }); - - it("background-position should set / get keyword length with offset", () => { - testPropertyValue("background-position", "top 10px left 20px", "left 20px top 10px"); - }); - - it("background-position should set / get multiple values", () => { - testPropertyValue("background-position", "left top, bottom right", "left top, right bottom"); - }); - - it("background-repeat should set / get keyword", () => { - testPropertyValue("background-repeat", "repeat", "repeat"); - }); - - it("background-repeat should set / get keyword keyword", () => { - testPropertyValue("background-repeat", "repeat no-repeat", "repeat-x"); - }); - - it("background-repeat should set / get keyword keyword", () => { - testPropertyValue("background-repeat", "no-repeat repeat", "repeat-y"); - }); - - it("background-repeat should set / get keyword keyword", () => { - testPropertyValue("background-repeat", "repeat repeat", "repeat"); - }); - - it("background-repeat should set / get keyword keyword", () => { - testPropertyValue("background-repeat", "repeat space", "repeat space"); - }); - - it("background-repeat should not set / get multiple axis keywords", () => { - testPropertyValue("background-repeat", "repeat-x repeat-y", ""); - }); - - it("background-repeat should set / get multiple values", () => { - testPropertyValue("background-repeat", "repeat, no-repeat", "repeat, no-repeat"); - }); - - it("background-size should set / get keyword", () => { - testPropertyValue("background-size", "contain", "contain"); - }); - - it("background-size should not set / get multiple ratio keywords", () => { - testPropertyValue("background-size", "contain cover", ""); - }); - - it("background-size should set / get keyword", () => { - testPropertyValue("background-size", "auto auto", "auto"); - }); - - it("background-size should set / get length and length", () => { - testPropertyValue("background-size", "10px 20px", "10px 20px"); - }); - - it("background-size should not set / get negative length", () => { - testPropertyValue("background-size", "-10px 20px", ""); - }); - - it("background-size should not set / get negative length", () => { - testPropertyValue("background-size", "10px -20px", ""); - }); - - it("background-size should set / get percent", () => { - testPropertyValue("background-size", "10% 20%", "10% 20%"); - }); - - it("background-size should not set / get negative percent", () => { - testPropertyValue("background-size", "-10% 20%", ""); - }); - - it("background-size should not set / get negative percent", () => { - testPropertyValue("background-size", "10% -20%%", ""); - }); - - it("background-size should set / get keyword and length", () => { - testPropertyValue("background-size", "auto 10px", "auto 10px"); - }); - - it("background-size should set / get length", () => { - testPropertyValue("background-size", "10px auto", "10px"); - }); - - it("background-size should set / get multiple values", () => { - testPropertyValue("background-size", "contain, cover", "contain, cover"); - }); - - it("background shorthand should set / get value", () => { - testImplicitPropertyValue( - "background", - "none", - "none", - new Map([ - ["background-image", "none"], - ["background-position", "0% 0%"], - ["background-size", "auto"], - ["background-repeat", "repeat"], - ["background-origin", "padding-box"], - ["background-clip", "border-box"], - ["background-attachment", "scroll"], - ["background-color", "transparent"] - ]) - ); - }); - - it("background shorthand should set / get value", () => { - testImplicitPropertyValue( - "background", - "transparent", - "transparent", - new Map([ - ["background-image", "none"], - ["background-position", "0% 0%"], - ["background-size", "auto"], - ["background-repeat", "repeat"], - ["background-origin", "padding-box"], - ["background-clip", "border-box"], - ["background-attachment", "scroll"], - ["background-color", "transparent"] - ]) - ); - }); - - it("background shorthand should set / get sub longhand values", () => { - testImplicitPropertyValue( - "background", - "fixed left / 50% repeat url(example.png) green", - 'url("example.png") left center / 50% fixed green', - new Map([ - ["background-image", 'url("example.png")'], - ["background-position", "left center"], - ["background-size", "50%"], - ["background-repeat", "repeat"], - ["background-origin", "padding-box"], - ["background-clip", "border-box"], - ["background-attachment", "fixed"], - ["background-color", "green"] - ]) - ); - }); - - it("background shorthand should set / get multiple values", () => { - testImplicitPropertyValue( - "background", - "fixed left / 50% repeat url(example.png), linear-gradient(to right, green, blue) green", - 'url("example.png") left center / 50% fixed, linear-gradient(to right, green, blue) green', - new Map([ - ["background-image", 'url("example.png"), linear-gradient(to right, green, blue)'], - ["background-position", "left center, 0% 0%"], - ["background-size", "50%, auto"], - ["background-repeat", "repeat, repeat"], - ["background-origin", "padding-box, padding-box"], - ["background-clip", "border-box, border-box"], - ["background-attachment", "fixed, scroll"], - ["background-color", "green"] - ]) - ); - }); - - it("background shorthand should set / get value", () => { - testImplicitPropertyValue( - "background", - "fixed left repeat url(example.png) var(--foo)", - "fixed left repeat url(example.png) var(--foo)", - new Map([ - ["background-image", ""], - ["background-position", ""], - ["background-size", ""], - ["background-repeat", ""], - ["background-origin", ""], - ["background-clip", ""], - ["background-attachment", ""], - ["background-color", ""] - ]) - ); - }); -}); - -describe("border", () => { - it("border-top-color should set / get color", () => { - testPropertyValue("border-top-color", "green", "green"); - }); - - it("border-top-color should set / get color", () => { - testPropertyValue("border-top-color", "#008000", "rgb(0, 128, 0)"); - }); - - it("border-top-color should set / get color", () => { - testPropertyValue("border-top-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); - - it("border-right-color should set / get color", () => { - testPropertyValue("border-right-color", "green", "green"); - }); - - it("border-right-color should set / get color", () => { - testPropertyValue("border-right-color", "#008000", "rgb(0, 128, 0)"); - }); - - it("border-right-color should set / get color", () => { - testPropertyValue("border-right-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); - - it("border-bottom-color should set / get color", () => { - testPropertyValue("border-bottom-color", "green", "green"); - }); - - it("border-bottom-color should set / get color", () => { - testPropertyValue("border-bottom-color", "#008000", "rgb(0, 128, 0)"); - }); - - it("border-bottom-color should set / get color", () => { - testPropertyValue("border-bottom-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); - - it("border-left-color should set / get color", () => { - testPropertyValue("border-left-color", "green", "green"); - }); - - it("border-left-color should set / get color", () => { - testPropertyValue("border-left-color", "#008000", "rgb(0, 128, 0)"); - }); - - it("border-left-color should set / get color", () => { - testPropertyValue("border-left-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); - - it("border-top-width should set / get keyword", () => { - testPropertyValue("border-top-width", "medium", "medium"); - }); - - it("border-top-width should set / get length", () => { - testPropertyValue("border-top-width", "10px", "10px"); - }); - - it("border-top-width should not set / get negative length", () => { - testPropertyValue("border-top-width", "-10px", ""); - }); - - it("border-right-width should set / get keyword", () => { - testPropertyValue("border-right-width", "medium", "medium"); - }); - - it("border-right-width should set / get length", () => { - testPropertyValue("border-right-width", "10px", "10px"); - }); - - it("border-right-width should not set / get negative length", () => { - testPropertyValue("border-right-width", "-10px", ""); - }); - - it("border-bottom-width should set / get keyword", () => { - testPropertyValue("border-bottom-width", "medium", "medium"); - }); - - it("border-bottom-width should set / get length", () => { - testPropertyValue("border-bottom-width", "10px", "10px"); - }); - - it("border-bottom-width should not set / get negative length", () => { - testPropertyValue("border-bottom-width", "-10px", ""); - }); - - it("border-left-width should set / get keyword", () => { - testPropertyValue("border-left-width", "medium", "medium"); - }); - - it("border-left-width should set / get length", () => { - testPropertyValue("border-left-width", "10px", "10px"); - }); - - it("border-left-width should not set / get negative length", () => { - testPropertyValue("border-left-width", "-10px", ""); - }); - - it("border-top-style should set / get keyword", () => { - testPropertyValue("border-top-style", "dotted", "dotted"); - }); - - it("border-right-style should set / get keyword", () => { - testPropertyValue("border-right-style", "dotted", "dotted"); - }); - - it("border-bottom-style should set / get keyword", () => { - testPropertyValue("border-bottom-style", "dotted", "dotted"); - }); - - it("border-left-style should set / get keyword", () => { - testPropertyValue("border-left-style", "dotted", "dotted"); - }); - - it("border-color should set / get color", () => { - testImplicitPropertyValue( - "border-color", - "green", - "green", - new Map([ - ["border-top-color", "green"], - ["border-right-color", "green"], - ["border-bottom-color", "green"], - ["border-left-color", "green"] - ]) - ); - }); - - it("border-color should set / get color", () => { - testImplicitPropertyValue( - "border-color", - "green blue", - "green blue", - new Map([ - ["border-top-color", "green"], - ["border-right-color", "blue"], - ["border-bottom-color", "green"], - ["border-left-color", "blue"] - ]) - ); - }); - - it("border-color should set / get color", () => { - testImplicitPropertyValue( - "border-color", - "green blue yellow", - "green blue yellow", - new Map([ - ["border-top-color", "green"], - ["border-right-color", "blue"], - ["border-bottom-color", "yellow"], - ["border-left-color", "blue"] - ]) - ); - }); - - it("border-color should set / get color", () => { - testImplicitPropertyValue( - "border-color", - "green blue yellow purple", - "green blue yellow purple", - new Map([ - ["border-top-color", "green"], - ["border-right-color", "blue"], - ["border-bottom-color", "yellow"], - ["border-left-color", "purple"] - ]) - ); - }); - - it("border-width should set / get keyword", () => { - testImplicitPropertyValue( - "border-width", - "thick", - "thick", - new Map([ - ["border-top-width", "thick"], - ["border-right-width", "thick"], - ["border-bottom-width", "thick"], - ["border-left-width", "thick"] - ]) - ); - }); - - it("border-width should set / get keyword", () => { - testImplicitPropertyValue( - "border-width", - "thick thin", - "thick thin", - new Map([ - ["border-top-width", "thick"], - ["border-right-width", "thin"], - ["border-bottom-width", "thick"], - ["border-left-width", "thin"] - ]) - ); - }); - - it("border-width should set / get keyword", () => { - testImplicitPropertyValue( - "border-width", - "thick thin medium", - "thick thin medium", - new Map([ - ["border-top-width", "thick"], - ["border-right-width", "thin"], - ["border-bottom-width", "medium"], - ["border-left-width", "thin"] - ]) - ); - }); - - it("border-width should set / get keyword and value", () => { - testImplicitPropertyValue( - "border-width", - "thick thin medium 10px", - "thick thin medium 10px", - new Map([ - ["border-top-width", "thick"], - ["border-right-width", "thin"], - ["border-bottom-width", "medium"], - ["border-left-width", "10px"] - ]) - ); - }); - - it("border-style should set / get keyword", () => { - testImplicitPropertyValue( - "border-style", - "dotted", - "dotted", - new Map([ - ["border-top-style", "dotted"], - ["border-right-style", "dotted"], - ["border-bottom-style", "dotted"], - ["border-left-style", "dotted"] - ]) - ); - }); - - it("border-style should set / get keyword", () => { - testImplicitPropertyValue( - "border-style", - "none", - "none", - new Map([ - ["border-top-style", "none"], - ["border-right-style", "none"], - ["border-bottom-style", "none"], - ["border-left-style", "none"] - ]) - ); - }); - - it("border-style should set / get keyword", () => { - testImplicitPropertyValue( - "border-style", - "dotted groove", - "dotted groove", - new Map([ - ["border-top-style", "dotted"], - ["border-right-style", "groove"], - ["border-bottom-style", "dotted"], - ["border-left-style", "groove"] - ]) - ); - }); - - it("border-style should set / get keyword", () => { - testImplicitPropertyValue( - "border-style", - "dotted groove double", - "dotted groove double", - new Map([ - ["border-top-style", "dotted"], - ["border-right-style", "groove"], - ["border-bottom-style", "double"], - ["border-left-style", "groove"] - ]) - ); - }); - - it("border-style should set / get keyword", () => { - testImplicitPropertyValue( - "border-style", - "dotted groove double none", - "dotted groove double none", - new Map([ - ["border-top-style", "dotted"], - ["border-right-style", "groove"], - ["border-bottom-style", "double"], - ["border-left-style", "none"] - ]) - ); - }); - - it("border-collapse should set / get keyword", () => { - testPropertyValue("border-collapse", "collapse", "collapse"); - }); - - it("border-spacing should set / get length", () => { - testPropertyValue("border-spacing", "10px", "10px"); - }); - - it("border-spacing should set / get length", () => { - testPropertyValue("border-spacing", "10px 20px", "10px 20px"); - }); - - it("border-top shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-top", - "1px solid green", - "1px solid green", - new Map([ - ["border-top-width", "1px"], - ["border-top-style", "solid"], - ["border-top-color", "green"] - ]) - ); - }); - - it("border-top shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-top", - "1px var(--foo) green", - "1px var(--foo) green", - new Map([ - ["border-top-width", ""], - ["border-top-style", ""], - ["border-top-color", ""] - ]) - ); - }); - - it("border-right shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-right", - "1px solid green", - "1px solid green", - new Map([ - ["border-right-width", "1px"], - ["border-right-style", "solid"], - ["border-right-color", "green"] - ]) - ); - }); - - it("border-right shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-right", - "1px var(--foo) green", - "1px var(--foo) green", - new Map([ - ["border-right-width", ""], - ["border-right-style", ""], - ["border-right-color", ""] - ]) - ); - }); - - it("border-bottom shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-bottom", - "1px solid green", - "1px solid green", - new Map([ - ["border-bottom-width", "1px"], - ["border-bottom-style", "solid"], - ["border-bottom-color", "green"] - ]) - ); - }); - - it("border-bottom shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-bottom", - "1px var(--foo) green", - "1px var(--foo) green", - new Map([ - ["border-bottom-width", ""], - ["border-bottom-style", ""], - ["border-bottom-color", ""] - ]) - ); - }); - - it("border-left shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-left", - "1px solid green", - "1px solid green", - new Map([ - ["border-left-width", "1px"], - ["border-left-style", "solid"], - ["border-left-color", "green"] - ]) - ); - }); - - it("border-left shorthand should set / get value", () => { - testImplicitPropertyValue( - "border-left", - "1px var(--foo) green", - "1px var(--foo) green", - new Map([ - ["border-left-width", ""], - ["border-left-style", ""], - ["border-left-color", ""] - ]) - ); - }); - - it("border shorthand should set / get value", () => { - testImplicitPropertyValue( - "border", - "1px solid green", - "1px solid green", - new Map([ - ["border-top-width", "1px"], - ["border-top-style", "solid"], - ["border-top-color", "green"], - ["border-right-width", "1px"], - ["border-right-style", "solid"], - ["border-right-color", "green"], - ["border-bottom-width", "1px"], - ["border-bottom-style", "solid"], - ["border-bottom-color", "green"], - ["border-left-width", "1px"], - ["border-left-style", "solid"], - ["border-left-color", "green"] - ]) - ); - }); - - it("border shorthand should set / get value", () => { - testImplicitPropertyValue( - "border", - "1px var(--foo) green", - "1px var(--foo) green", - new Map([ - ["border-top-width", ""], - ["border-top-style", ""], - ["border-top-color", ""], - ["border-right-width", ""], - ["border-right-style", ""], - ["border-right-color", ""], - ["border-bottom-width", ""], - ["border-bottom-style", ""], - ["border-bottom-color", ""], - ["border-left-width", ""], - ["border-left-style", ""], - ["border-left-color", ""] - ]) - ); - }); -}); - -describe("box model", () => { - it("margin-top should set / get keyword", () => { - testPropertyValue("margin-top", "auto", "auto"); - }); - - it("margin-top should set / get length", () => { - testPropertyValue("margin-top", "0", "0px"); - }); - - it("margin-top should set / get length", () => { - testPropertyValue("margin-top", "10px", "10px"); - }); - - it("margin-top should set / get negative length", () => { - testPropertyValue("margin-top", "-10px", "-10px"); - }); - - it("margin-top should set / get percent", () => { - testPropertyValue("margin-top", "10%", "10%"); - }); - - it("margin-top should set / get negative percent", () => { - testPropertyValue("margin-top", "-10%", "-10%"); - }); - - it("margin-right should set / get keyword", () => { - testPropertyValue("margin-right", "auto", "auto"); - }); - - it("margin-right should set / get length", () => { - testPropertyValue("margin-right", "0", "0px"); - }); - - it("margin-right should set / get length", () => { - testPropertyValue("margin-right", "10px", "10px"); - }); - - it("margin-right should set / get negative length", () => { - testPropertyValue("margin-right", "-10px", "-10px"); - }); - - it("margin-right should set / get percent", () => { - testPropertyValue("margin-right", "10%", "10%"); - }); - - it("margin-right should set / get negative percent", () => { - testPropertyValue("margin-right", "-10%", "-10%"); - }); - - it("margin-bottom should set / get keyword", () => { - testPropertyValue("margin-bottom", "auto", "auto"); - }); - - it("margin-bottom should set / get length", () => { - testPropertyValue("margin-bottom", "0", "0px"); - }); - - it("margin-bottom should set / get length", () => { - testPropertyValue("margin-bottom", "10px", "10px"); - }); - - it("margin-bottom should set / get negative length", () => { - testPropertyValue("margin-bottom", "-10px", "-10px"); - }); - - it("margin-bottom should set / get percent", () => { - testPropertyValue("margin-bottom", "10%", "10%"); - }); - - it("margin-bottom should set / get negative percent", () => { - testPropertyValue("margin-bottom", "-10%", "-10%"); - }); - - it("margin-left should set / get keyword", () => { - testPropertyValue("margin-left", "auto", "auto"); - }); - - it("margin-left should set / get length", () => { - testPropertyValue("margin-left", "0", "0px"); - }); - - it("margin-left should set / get length", () => { - testPropertyValue("margin-left", "10px", "10px"); - }); - - it("margin-left should set / get negative length", () => { - testPropertyValue("margin-left", "-10px", "-10px"); - }); - - it("margin-left should set / get percent", () => { - testPropertyValue("margin-left", "10%", "10%"); - }); - - it("margin-left should set / get negative percent", () => { - testPropertyValue("margin-left", "-10%", "-10%"); - }); - - it("margin shorthand should set / get keyword", () => { - testImplicitPropertyValue( - "margin", - "auto", - "auto", - new Map([ - ["margin-top", "auto"], - ["margin-right", "auto"], - ["margin-bottom", "auto"], - ["margin-left", "auto"] - ]) - ); - }); - - it("margin shorthand should set / get value", () => { - testImplicitPropertyValue( - "margin", - "10px", - "10px", - new Map([ - ["margin-top", "10px"], - ["margin-right", "10px"], - ["margin-bottom", "10px"], - ["margin-left", "10px"] - ]) - ); - }); - - it("margin shorthand should set / get value", () => { - testImplicitPropertyValue( - "margin", - "10px 20px", - "10px 20px", - new Map([ - ["margin-top", "10px"], - ["margin-right", "20px"], - ["margin-bottom", "10px"], - ["margin-left", "20px"] - ]) - ); - }); - - it("margin shorthand should set / get value", () => { - testImplicitPropertyValue( - "margin", - "10px 20px 30px", - "10px 20px 30px", - new Map([ - ["margin-top", "10px"], - ["margin-right", "20px"], - ["margin-bottom", "30px"], - ["margin-left", "20px"] - ]) - ); - }); - - it("margin shorthand should set / get value", () => { - testImplicitPropertyValue( - "margin", - "10px 20px 30px 40px", - "10px 20px 30px 40px", - new Map([ - ["margin-top", "10px"], - ["margin-right", "20px"], - ["margin-bottom", "30px"], - ["margin-left", "40px"] - ]) - ); - }); - - it("margin shorthand should set / get value", () => { - testImplicitPropertyValue( - "margin", - "10px var(--foo)", - "10px var(--foo)", - new Map([ - ["margin-top", ""], - ["margin-right", ""], - ["margin-bottom", ""], - ["margin-left", ""] - ]) - ); - }); - - it("margin shorthand should set / get value", () => { - testImplicitPropertyValue( - "margin", - "initial", - "initial", - new Map([ - ["margin-top", "initial"], - ["margin-right", "initial"], - ["margin-bottom", "initial"], - ["margin-left", "initial"] - ]) - ); - }); - - it("padding-top should set / get length", () => { - testPropertyValue("padding-top", "0", "0px"); - }); - - it("padding-top should set / get length", () => { - testPropertyValue("padding-top", "10px", "10px"); - }); - - it("padding-top should not set / get negative length", () => { - testPropertyValue("padding-top", "-10px", ""); - }); - - it("padding-top should set / get percent", () => { - testPropertyValue("padding-top", "10%", "10%"); - }); - - it("padding-top should not set / get negative percent", () => { - testPropertyValue("padding-top", "-10%", ""); - }); - - it("padding-right should set / get length", () => { - testPropertyValue("padding-right", "0", "0px"); - }); - - it("padding-right should set / get length", () => { - testPropertyValue("padding-right", "10px", "10px"); - }); - - it("padding-right should not set / get negative length", () => { - testPropertyValue("padding-right", "-10px", ""); - }); - - it("padding-right should set / get percent", () => { - testPropertyValue("padding-right", "10%", "10%"); - }); - - it("padding-right should not set / get negative percent", () => { - testPropertyValue("padding-right", "-10%", ""); - }); - - it("padding-bottom should set / get length", () => { - testPropertyValue("padding-bottom", "0", "0px"); - }); - - it("padding-bottom should set / get length", () => { - testPropertyValue("padding-bottom", "10px", "10px"); - }); - - it("padding-bottom should not set / get negative length", () => { - testPropertyValue("padding-bottom", "-10px", ""); - }); - - it("padding-bottom should set / get percent", () => { - testPropertyValue("padding-bottom", "10%", "10%"); - }); - - it("padding-bottom should not set / get negative percent", () => { - testPropertyValue("padding-bottom", "-10%", ""); - }); - - it("padding-left should set / get length", () => { - testPropertyValue("padding-left", "0", "0px"); - }); - - it("padding-left should set / get length", () => { - testPropertyValue("padding-left", "10px", "10px"); - }); - - it("padding-left should not set / get negative length", () => { - testPropertyValue("padding-left", "-10px", ""); - }); - - it("padding-left should set / get percent", () => { - testPropertyValue("padding-left", "10%", "10%"); - }); - - it("padding-left should not set / get negative percent", () => { - testPropertyValue("padding-left", "-10%", ""); - }); - - it("padding shorthand should set / get value", () => { - testImplicitPropertyValue( - "padding", - "10px", - "10px", - new Map([ - ["padding-top", "10px"], - ["padding-right", "10px"], - ["padding-bottom", "10px"], - ["padding-left", "10px"] - ]) - ); - }); - - it("padding shorthand should set / get value", () => { - testImplicitPropertyValue( - "padding", - "10px 20px", - "10px 20px", - new Map([ - ["padding-top", "10px"], - ["padding-right", "20px"], - ["padding-bottom", "10px"], - ["padding-left", "20px"] - ]) - ); - }); - - it("padding shorthand should set / get value", () => { - testImplicitPropertyValue( - "padding", - "10px 20px 30px", - "10px 20px 30px", - new Map([ - ["padding-top", "10px"], - ["padding-right", "20px"], - ["padding-bottom", "30px"], - ["padding-left", "20px"] - ]) - ); - }); - - it("padding shorthand should set / get value", () => { - testImplicitPropertyValue( - "padding", - "10px 20px 30px 40px", - "10px 20px 30px 40px", - new Map([ - ["padding-top", "10px"], - ["padding-right", "20px"], - ["padding-bottom", "30px"], - ["padding-left", "40px"] - ]) - ); - }); - - it("padding shorthand should set / get value", () => { - testImplicitPropertyValue( - "padding", - "10px var(--foo)", - "10px var(--foo)", - new Map([ - ["padding-top", ""], - ["padding-right", ""], - ["padding-bottom", ""], - ["padding-left", ""] - ]) - ); - }); - - it("padding shorthand should set / get value", () => { - testImplicitPropertyValue( - "padding", - "initial", - "initial", - new Map([ - ["padding-top", "initial"], - ["padding-right", "initial"], - ["padding-bottom", "initial"], - ["padding-left", "initial"] - ]) - ); - }); -}); - -describe("box sizing", () => { - it("height should set / get keyword", () => { - testPropertyValue("height", "auto", "auto"); - }); - - it("height should set / get length", () => { - testPropertyValue("height", "0", "0px"); - }); - - it("height should set / get length", () => { - testPropertyValue("height", "10px", "10px"); - }); - - it("height should not set / get negative length", () => { - testPropertyValue("height", "-10px", ""); - }); - - it("height should set / get percent", () => { - testPropertyValue("height", "10%", "10%"); - }); - - it("height should not set / get negative percent", () => { - testPropertyValue("height", "-10%", ""); - }); - - it("width should set / get keyword", () => { - testPropertyValue("width", "auto", "auto"); - }); - - it("height should set / get length", () => { - testPropertyValue("width", "0", "0px"); - }); - - it("width should set / get length", () => { - testPropertyValue("width", "10px", "10px"); - }); - - it("width should not set / get negative length", () => { - testPropertyValue("width", "-10px", ""); - }); - - it("width should set / get percent", () => { - testPropertyValue("width", "10%", "10%"); - }); - - it("width should not set / get negative percent", () => { - testPropertyValue("width", "-10%", ""); - }); -}); - -describe("color", () => { - it("color should set / get color name", () => { - testPropertyValue("color", "green", "green"); - }); - - it("color should set / get hex color", () => { - testPropertyValue("color", "#008000", "rgb(0, 128, 0)"); - }); - - it("color should set / get color function", () => { - testPropertyValue("color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); - - it("color should set / get color function", () => { - testPropertyValue( - "color", - "light-dark(#008000, #0000ff)", - "light-dark(rgb(0, 128, 0), rgb(0, 0, 255))" - ); - }); - - it("color should not should set / get invalid value", () => { - testPropertyValue( - "color", - "color-mix(in hsl, hsl(120deg 10% 20%) 0%, hsl(30deg 30% 40%) 0%)", - "" - ); - }); - - it("color should not should set / get invalid value", () => { - testPropertyValue("color", "color(srgb 0 0 0 0)", ""); - }); - - it("opacity should set / get keyword", () => { - testPropertyValue("opacity", "inherit", "inherit"); - }); - - it("opacity should set / get number", () => { - testPropertyValue("opacity", "0.5", "0.5"); - }); - - it("opacity should set / get number", () => { - testPropertyValue("opacity", ".5", "0.5"); - }); - - it("opacity should set / get number", () => { - testPropertyValue("opacity", "1.5", "1.5"); - }); - - it("opacity should set / get number", () => { - testPropertyValue("opacity", "-1", "-1"); - }); - - it("opacity should set / get percent", () => { - testPropertyValue("opacity", "50%", "50%"); - }); - - it("opacity should set / get percent", () => { - testPropertyValue("opacity", "150%", "150%"); - }); - - it("opacity should set / get percent", () => { - testPropertyValue("opacity", "-50%", "-50%"); - }); -}); - -describe("display", () => { - it("display should set / get keyword", () => { - testPropertyValue("display", "block", "block"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline", "inline"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline-block", "inline-block"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flex", "flex"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline-flex", "inline-flex"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "grid", "grid"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline-grid", "inline-grid"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow", "block"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow-root", "flow-root"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "none", "none"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "contents", "contents"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "table-row", "table-row"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow list-item", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "list-item flow", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block list-item", "list-item"); - }); - - it("display should not set / get keyword", () => { - testPropertyValue("display", "list-item block", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow-root list-item", "flow-root list-item"); - }); - - it("display should not set / get keyword", () => { - testPropertyValue("display", "list-item flow-root", "flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block flow list-item", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block list-item flow", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block flow-root list-item", "flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block list-item flow-root", "flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow block list-item", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow list-item block", "list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow-root block list-item", "flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow-root list-item block", "flow-root list-item"); - }); - - it("display should not set / get keyword", () => { - testPropertyValue("display", "list-item block flow", "list-item"); - }); - - it("display should not set / get keyword", () => { - testPropertyValue("display", "list-item flow block", "list-item"); - }); - - it("display should not set / get keyword", () => { - testPropertyValue("display", "list-item block flow-root", "flow-root list-item"); - }); - - it("display should not set / get keyword", () => { - testPropertyValue("display", "list-item flow-root block", "flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline flow list-item", "inline list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline flow-root list-item", "inline flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "run-in flow list-item", "run-in list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "run-in flow-root list-item", "run-in flow-root list-item"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block flow", "block"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow block", "block"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block flow-root", "flow-root"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block flex", "flex"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "block ruby", "block ruby"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline flow", "inline"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow inline", "inline"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline flow-root", "inline-block"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline flex", "inline-flex"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "inline ruby", "ruby"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "run-in flow", "run-in"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "flow run-in", "run-in"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "run-in flow-root", "run-in flow-root"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "run-in flex", "run-in flex"); - }); - - it("display should set / get keyword", () => { - testPropertyValue("display", "run-in ruby", "run-in ruby"); - }); -}); - -describe("flex box", () => { - it("flex-grow should set / get number", () => { - testPropertyValue("flex-grow", ".5", "0.5"); - }); - - it("flex-grow should not set / get negative number", () => { - testPropertyValue("flex-grow", "-1", ""); - }); - - it("flex-shrink should set / get number", () => { - testPropertyValue("flex-shrink", ".5", "0.5"); - }); - - it("flex-shrink should not set / get negative number", () => { - testPropertyValue("flex-shrink", "-1", ""); - }); - - it("flex-basis should set / get keyword", () => { - testPropertyValue("flex-basis", "content", "content"); - }); - - it("flex-basis should not set / get length", () => { - testPropertyValue("flex-basis", "10px", "10px"); - }); - - it("flex-basis should not set / get length", () => { - testPropertyValue("flex-basis", "0", "0px"); - }); - - it("flex-basis should not set / get percent", () => { - testPropertyValue("flex-basis", "10%", "10%"); - }); - - it("flex-basis should not set / get percent", () => { - testPropertyValue("flex-basis", "0%", "0%"); - }); - - it("flex should set / get keyword", () => { - testPropertyValue("flex", "none", "0 0 auto"); - }); - - it("flex should set / get keyword", () => { - testPropertyValue("flex", "auto", "1 1 auto"); - }); - - it("flex should set / get keyword", () => { - testPropertyValue("flex", "initial", "initial"); - }); - - it("flex should set / get keyword", () => { - testPropertyValue("flex", "unset", "unset"); - }); - - it("flex shorthand should not set / get longhand value", () => { - testPropertyValue("flex", "2 1 3", ""); - }); - - it("flex shorthand should not set / get longhand value", () => { - testPropertyValue("flex", "2 1 calc(3)", ""); - }); - - it("flex shorthand should set / get longhand value", () => { - testPropertyValue("flex", "2", "2 1 0%"); - }); - - it("flex shorthand should set / get longhand value", () => { - testPropertyValue("flex", "10%", "1 1 10%"); - }); - - it("flex shorthand should set / get longhand value", () => { - testPropertyValue("flex", "2 10px", "2 1 10px"); - }); - - it("flex shorthand should set / get longhand value", () => { - testPropertyValue("flex", "2 3", "2 3 0%"); - }); - - it("flex shorthand should set / get longhand value", () => { - testPropertyValue("flex", "2 3 10px", "2 3 10px"); - }); - - it("flex shorthand should set / get longhand value", () => { - testImplicitPropertyValue( - "flex", - "2 var(--foo) 10px", - "2 var(--foo) 10px", - new Map([ - ["flex-grow", ""], - ["flex-shrink", ""], - ["flex-basis", ""] - ]) - ); - }); - - it("flex shorthand should set / get longhand value", () => { - testImplicitPropertyValue( - "flex", - "calc(2px * 3)", - "1 1 calc(6px)", - new Map([ - ["flex-grow", "1"], - ["flex-shrink", "1"], - ["flex-basis", "calc(6px)"] - ]) - ); - }); - - it("flex shorthand should set / get longhand value", () => { - testImplicitPropertyValue( - "flex", - "calc(2 * 3)", - "calc(6) 1 0%", - new Map([ - ["flex-grow", "calc(6)"], - ["flex-shrink", "1"], - ["flex-basis", "0%"] - ]) - ); - }); - - it("flex shorthand should set / get longhand value", () => { - testImplicitPropertyValue( - "flex", - "initial", - "initial", - new Map([ - ["flex-grow", "initial"], - ["flex-shrink", "initial"], - ["flex-basis", "initial"] - ]) - ); - }); -}); - -describe("font", () => { - it("font-style should set / get keyword", () => { - testPropertyValue("font-style", "italic", "italic"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "normal", "normal"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "none", "none"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "none", "none"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "common-ligatures", "common-ligatures"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "no-common-ligatures", "no-common-ligatures"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "small-caps", "small-caps"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "stylistic(flowing)", "stylistic(flowing)"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue( - "font-variant", - "stylistic(flowing) historical-forms styleset(flowing) character-variant(flowing) swash(flowing) ornaments(flowing) annotation(flowing)", - "stylistic(flowing) historical-forms styleset(flowing) character-variant(flowing) swash(flowing) ornaments(flowing) annotation(flowing)" - ); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "jis78", "jis78"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "ruby", "ruby"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "sub", "sub"); - }); - - it("font-variant should set / get keyword", () => { - testPropertyValue("font-variant", "super", "super"); - }); - - it("font-variant should not set / get invalid keywords", () => { - testPropertyValue("font-variant", "normal none", ""); - }); - - it("font-variant should not set / get invalid keywords", () => { - testPropertyValue("font-variant", "normal small-caps", ""); - }); - - it("font-variant should not set / get invalid keywords", () => { - testPropertyValue("font-variant", "none small-caps", ""); - }); - - it("font-weight should set / get keyword", () => { - testPropertyValue("font-weight", "bold", "bold"); - }); - - it("font-weight should set / get number", () => { - testPropertyValue("font-weight", "400", "400"); - }); - - it("font-weight should not set / get number greater than 1000", () => { - testPropertyValue("font-weight", "1001", ""); - }); - - it("font-weight should not set / get negative number", () => { - testPropertyValue("font-weight", "-1", ""); - }); - - it("font-size should set / get keyword", () => { - testPropertyValue("font-size", "medium", "medium"); - }); - - it("font-size should set / get keyword", () => { - testPropertyValue("font-size", "larger", "larger"); - }); - - it("font-size should set / get length", () => { - testPropertyValue("font-size", "1em", "1em"); - }); - - it("font-size not should set / get negative length", () => { - testPropertyValue("font-size", "-1em", ""); - }); - - it("font-size should set / get percent", () => { - testPropertyValue("font-size", "90%", "90%"); - }); - - it("font-size should not set / get negative percent", () => { - testPropertyValue("font-size", "-10%", ""); - }); - - it("line-height should set / get keyword", () => { - testPropertyValue("line-height", "normal", "normal"); - }); - - it("line-height should set / get number", () => { - testPropertyValue("line-height", "1.2", "1.2"); - }); - - it("line-height should not set / get negative number", () => { - testPropertyValue("line-height", "-1", ""); - }); - - it("line-height should set / get length", () => { - testPropertyValue("line-height", "10px", "10px"); - }); - - it("line-height should not set / get negative length", () => { - testPropertyValue("line-height", "-10px", ""); - }); - - it("line-height should set / get percent", () => { - testPropertyValue("line-height", "10%", "10%"); - }); - - it("line-height should not set / get negative percent", () => { - testPropertyValue("line-height", "-10%", ""); - }); - - it("font-family should set / get keyword", () => { - testPropertyValue("font-family", "sans-serif", "sans-serif"); - }); - - it("font-family should set / get keyword", () => { - testPropertyValue("font-family", '"sans-serif"', '"sans-serif"'); - }); - - it("font-family should set / get family name", () => { - testPropertyValue("font-family", "Times", "Times"); - }); - - it("font-family should set / get quoted family name", () => { - testPropertyValue("font-family", '"Times New Roman"', '"Times New Roman"'); - }); - - it("font-family should set / get family values", () => { - testPropertyValue( - "font-family", - 'Times, "Times New Roman", Georgia, serif', - 'Times, "Times New Roman", Georgia, serif' - ); - }); - - it("font-family should set / get family values", () => { - testPropertyValue("font-family", "Times\\ New Roman, serif", '"Times New Roman", serif'); - }); - - it("font-family should set / get family values", () => { - testPropertyValue("font-family", '"Times\\ New Roman", serif', '"Times New Roman", serif'); - }); - - it("font-family should set / get family values", () => { - testPropertyValue( - "font-family", - '"Gill Sans Extrabold", sans-serif', - '"Gill Sans Extrabold", sans-serif' - ); - }); - - it("font-family should set / get family values", () => { - testPropertyValue( - "font-family", - '"Goudy Bookletter 1911", sans-serif', - '"Goudy Bookletter 1911", sans-serif' - ); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "Goudy Bookletter 1911, sans-serif", ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "Red/Black, sans-serif", ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", '"Lucida" Grande, sans-serif', ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", 'Lucida "Grande", sans-serif', ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "Ahem!, sans-serif", ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "test@foo, sans-serif", ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "#POUND, sans-serif", ""); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "Hawaii 5-0, sans-serif", ""); - }); - - it("font-family should set / get family values", () => { - testPropertyValue("font-family", "generic(fangsong)", "generic(fangsong)"); - }); - - it("font-family should set / get family values", () => { - testPropertyValue("font-family", "generic(kai)", "generic(kai)"); - }); - - it("font-family should set / get family values", () => { - testPropertyValue("font-family", "generic(khmer-mul)", "generic(khmer-mul)"); - }); - - it("font-family should set / get family values", () => { - testPropertyValue("font-family", "generic(nastaliq)", "generic(nastaliq)"); - }); - - it("font-family should not set / get invalid family values", () => { - testPropertyValue("font-family", "generic(foo)", ""); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - 'normal medium Times, "Times New Roman", Georgia, serif', - 'medium Times, "Times New Roman", Georgia, serif', - new Map([ - ["font-style", "normal"], - ["font-variant", "normal"], - ["font-weight", "normal"], - ["font-size", "medium"], - ["line-height", "normal"], - ["font-family", 'Times, "Times New Roman", Georgia, serif'] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - "normal medium Gill Sans Extrabold, sans-serif", - 'medium "Gill Sans Extrabold", sans-serif', - new Map([ - ["font-style", "normal"], - ["font-variant", "normal"], - ["font-weight", "normal"], - ["font-size", "medium"], - ["line-height", "normal"], - ["font-family", '"Gill Sans Extrabold", sans-serif'] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - 'normal medium "Goudy Bookletter 1911", sans-serif', - 'medium "Goudy Bookletter 1911", sans-serif', - new Map([ - ["font-style", "normal"], - ["font-variant", "normal"], - ["font-weight", "normal"], - ["font-size", "medium"], - ["line-height", "normal"], - ["font-family", '"Goudy Bookletter 1911", sans-serif'] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - "normal medium generic(fangsong)", - "medium generic(fangsong)", - new Map([ - ["font-style", "normal"], - ["font-variant", "normal"], - ["font-weight", "normal"], - ["font-size", "medium"], - ["line-height", "normal"], - ["font-family", "generic(fangsong)"] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - "normal medium Goudy Bookletter 1911, sans-serif", - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - "normal medium Red/Black, sans-serif", - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - 'normal medium "Lucida" Grande, sans-serif', - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - 'normal medium Lucida "Grande", sans-serif', - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - "normal medium Ahem!, sans-serif", - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - "normal medium test@foo, sans-serif", - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - "normal medium #POUND, sans-serif", - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should not set / get invalid values", () => { - testImplicitPropertyValue( - "font", - "normal medium Hawaii 5-0, sans-serif", - "", - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - 'italic bold medium/1.2 Times, "Times New Roman", Georgia, serif', - 'italic bold medium / 1.2 Times, "Times New Roman", Georgia, serif', - new Map([ - ["font-style", "italic"], - ["font-variant", "normal"], - ["font-weight", "bold"], - ["font-size", "medium"], - ["line-height", "1.2"], - ["font-family", 'Times, "Times New Roman", Georgia, serif'] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - 'italic bold calc(3em/2)/1.2 Times, "Times New Roman", Georgia, serif', - 'italic bold calc(1.5em) / 1.2 Times, "Times New Roman", Georgia, serif', - new Map([ - ["font-style", "italic"], - ["font-variant", "normal"], - ["font-weight", "bold"], - ["font-size", "calc(1.5em)"], - ["line-height", "1.2"], - ["font-family", 'Times, "Times New Roman", Georgia, serif'] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - 'italic bold var(--foo)/1.2 Times, "Times New Roman", Georgia, serif', - 'italic bold var(--foo)/1.2 Times, "Times New Roman", Georgia, serif', - new Map([ - ["font-style", ""], - ["font-variant", ""], - ["font-weight", ""], - ["font-size", ""], - ["line-height", ""], - ["font-family", ""] - ]) - ); - }); - - it("font shorthand should set / get values", () => { - testImplicitPropertyValue( - "font", - "initial", - "initial", - new Map([ - ["font-style", "initial"], - ["font-variant", "initial"], - ["font-weight", "initial"], - ["font-size", "initial"], - ["line-height", "initial"], - ["font-family", "initial"] - ]) - ); - }); -}); - -describe("logical", () => { - it("clear should set / get keyword", () => { - testPropertyValue("clear", "left", "left"); - }); - - it("clear should set / get keyword", () => { - testPropertyValue("clear", "none", "none"); - }); - - it("float should set / get keyword", () => { - testPropertyValue("float", "left", "left"); - }); - - it("float should set / get keyword", () => { - testPropertyValue("float", "none", "none"); - }); -}); - -describe("masking", () => { - it("clip should set / get keyword", () => { - testPropertyValue("clip", "auto", "auto"); - }); - - it("clip should set / get legacy ", () => { - testPropertyValue("clip", "rect(0, 10px, 20%, 40EM)", "rect(0px, 10px, 20%, 40em)"); - }); -}); - -describe("positioning", () => { - it("bottom should set / get keyword", () => { - testPropertyValue("bottom", "auto", "auto"); - }); - - it("bottom should set / get length", () => { - testPropertyValue("bottom", "10px", "10px"); - }); - - it("bottom should set / get percent", () => { - testPropertyValue("bottom", "10%", "10%"); - }); - - it("left should set / get keyword", () => { - testPropertyValue("left", "auto", "auto"); - }); - - it("left should set / get length", () => { - testPropertyValue("left", "10px", "10px"); - }); - - it("left should set / get percent", () => { - testPropertyValue("left", "10%", "10%"); - }); - - it("right should set / get keyword", () => { - testPropertyValue("right", "auto", "auto"); - }); - - it("right should set / get length", () => { - testPropertyValue("right", "10px", "10px"); - }); - - it("right should set / get percent", () => { - testPropertyValue("right", "10%", "10%"); - }); - - it("top should set / get keyword", () => { - testPropertyValue("top", "auto", "auto"); - }); - - it("top should set / get length", () => { - testPropertyValue("top", "10px", "10px"); - }); - - it("top should set / get percent", () => { - testPropertyValue("top", "10%", "10%"); - }); -}); - -describe("user interface", () => { - it("outline-color should set / get color name", () => { - testPropertyValue("outline-color", "green", "green"); - }); - - it("outline-color should set / get hex color", () => { - testPropertyValue("outline-color", "#008000", "rgb(0, 128, 0)"); - }); - - it("outline-color should set / get color function", () => { - testPropertyValue("outline-color", "rgb(0 128 0)", "rgb(0, 128, 0)"); - }); -}); - -/* regression tests */ -describe("regression test for https://github.com/jsdom/jsdom/issues/3833", () => { - it("should set global value unset", () => { - const style = new CSSStyleProperties(window); - style.setProperty("width", "10px"); - assert.strictEqual(style.getPropertyValue("width"), "10px"); - - style.setProperty("width", "unset"); - assert.strictEqual(style.getPropertyValue("width"), "unset"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3878", () => { - it("should not set custom properties twice", () => { - const style = new CSSStyleProperties(window); - style.setProperty("--foo", 0); - style.setProperty("--foo", 1); - - assert.strictEqual(style.length, 1); - assert.strictEqual(style.item(0), "--foo"); - assert.strictEqual(style.item(1), ""); - assert.strictEqual(style.getPropertyValue("--foo"), "1"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/129", () => { - it("should set stringified value", () => { - const style = new CSSStyleProperties(window); - style.setProperty("--foo", true); - assert.strictEqual(style.getPropertyValue("--foo"), "true"); - }); - - it("throws for setting Symbol", () => { - const style = new CSSStyleProperties(window); - assert.throws( - () => style.setProperty("width", Symbol("foo")), - (e) => { - assert.strictEqual(e instanceof TypeError, true); - assert.strictEqual(e.message, "Can not convert symbol to string."); - return true; - } - ); - assert.throws( - () => { - style.width = Symbol("foo"); - }, - (e) => { - assert.strictEqual(e instanceof TypeError, true); - assert.strictEqual(e.message, "Can not convert symbol to string."); - return true; - } - ); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/70", () => { - it('returns empty string for "webkit-*", without leading "-"', () => { - const style = new CSSStyleProperties(window); - style.cssText = "background-color: green; webkit-transform: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitTransform, ""); - }); - - it('should set/get value for "-webkit-*"', () => { - const style = new CSSStyleProperties(window); - style.cssText = "background-color: green; -webkit-transform: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitTransform, "scale(3)"); - }); - - it('returns undefined for unknown "-webkit-*"', () => { - const style = new CSSStyleProperties(window); - style.cssText = "background-color: green; -webkit-foo: scale(3);"; - assert.strictEqual(style.backgroundColor, "green"); - assert.strictEqual(style.webkitFoo, undefined); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/124", () => { - it("no-op when setting undefined to border", () => { - const style = new CSSStyleProperties(window); - style.border = "1px solid green"; - assert.strictEqual(style.border, "1px solid green"); - style.border = undefined; - assert.strictEqual(style.border, "1px solid green"); - }); - - it("no-op when setting undefined to borderWidth", () => { - const style = new CSSStyleProperties(window); - style.borderWidth = "1px"; - assert.strictEqual(style.borderWidth, "1px"); - style.border = undefined; - assert.strictEqual(style.borderWidth, "1px"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/212", () => { - it("should support keywords", () => { - const keywords = [ - "serif", - "sans-serif", - "cursive", - "fantasy", - "monospace", - "system-ui", - "math", - "ui-serif", - "ui-sans-serif", - "ui-monospace", - "ui-rounded" - ]; - const style = new CSSStyleProperties(window); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - it("should support generic() function keywords", () => { - const keywords = [ - "generic(fangsong)", - "generic(kai)", - "generic(khmer-mul)", - "generic(nastaliq)" - ]; - const style = new CSSStyleProperties(window); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - // see https://drafts.csswg.org/css-fonts-4/#changes-2021-12-21 - it("should support removed generic keywords as non generic family name", () => { - const keywords = ["emoji", "fangsong"]; - const style = new CSSStyleProperties(window); - for (const keyword of keywords) { - style.fontFamily = keyword; - assert.strictEqual(style.fontFamily, keyword); - } - }); - - it("should support `-webkit-` prefixed family name", () => { - const style = new CSSStyleProperties(window); - style.fontFamily = "-webkit-body"; - assert.strictEqual(style.fontFamily, "-webkit-body"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3021", () => { - it("should get normalized value for font shorthand", () => { - const style = new CSSStyleProperties(window); - style.font = "normal bold 4px sans-serif"; - assert.strictEqual(style.font, "bold 4px sans-serif"); - }); -}); - -describe("regression test for https://github.com/jsdom/cssstyle/issues/214", () => { - it("should return value for each property", () => { - const style = new CSSStyleProperties(window); - const key = "background-color"; - const camel = "backgroundColor"; - const value = "var(--foo)"; - style[key] = value; - assert.strictEqual(style[key], value); - style[key] = null; - style[camel] = value; - assert.strictEqual(style[camel], value); - }); - - it("should set var() values for background-attachment correctly", () => { - const style = new CSSStyleProperties(window); - style.backgroundAttachment = "var(--foo)"; - assert.strictEqual(style.backgroundAttachment, "var(--foo)"); - style.setProperty("background-attachment", "var(--bar)"); - assert.strictEqual(style.backgroundAttachment, "var(--bar)"); - }); - - it("should allow changing a single property on a border, when border contains a css variable", () => { - const style = new CSSStyleProperties(window); - style.border = "0.1rem solid var(--my-color-value)"; - assert.strictEqual(style.border, "0.1rem solid var(--my-color-value)"); - style.borderWidth = "0.2rem"; - assert.strictEqual(style.borderWidth, "0.2rem"); - assert.strictEqual(style.border, ""); - }); - - it("should get value and priority", () => { - const style = new CSSStyleProperties(window); - style.cssText = "word-spacing: 1px !important;"; - assert.strictEqual(style.cssText, "word-spacing: 1px !important;", "cssText"); - assert.strictEqual(style.getPropertyValue("word-spacing"), "1px", "value"); - assert.strictEqual(style.getPropertyPriority("word-spacing"), "important", "priority"); - }); -}); - -describe("regression test for https://github.com/jsdom/jsdom/issues/3944", () => { - it("should get overwritten value", () => { - const style = new CSSStyleProperties(window); - style.cssText = - "background: linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center;"; - assert.strictEqual( - style.cssText, - "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(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.cssText, - "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center top;" - ); - assert.strictEqual(style.backgroundPosition, "center top"); - }); - - it("should not overwrite value", () => { - const style = new CSSStyleProperties(window); - style.cssText = - "background: linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center !important; background-position: top;"; - assert.strictEqual( - style.cssText, - "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center center !important;" - ); - assert.strictEqual(style.backgroundPosition, "center center"); - }); - - it("should get overwritten value", () => { - const style = new CSSStyleProperties(window); - style.cssText = - "background:linear-gradient(rgb(0 0 255 / 0.5), rgb(255 255 0 / 0.5)) center center !important; background-position: top !important;"; - assert.strictEqual( - style.cssText, - "background: linear-gradient(rgba(0, 0, 255, 0.5), rgba(255, 255, 0, 0.5)) center top !important;" - ); - assert.strictEqual(style.backgroundPosition, "center top"); - }); -}); From e57b9902d119fff517294fd18b57e9b100899725 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 13 Nov 2025 18:34:10 +0900 Subject: [PATCH 04/29] Rename propertyList to propertyDefinitions --- .gitignore | 2 +- lib/CSSStyleDeclaration.js | 8 ++++---- lib/index.js | 4 ++-- lib/normalize.js | 4 ++-- package.json | 3 +-- ...ropertyList.mjs => generatePropertyDefinitions.mjs} | 8 ++++---- test/CSSStyleDeclaration.test.js | 10 +++++----- 7 files changed, 19 insertions(+), 20 deletions(-) rename scripts/{generatePropertyList.mjs => generatePropertyDefinitions.mjs} (87%) diff --git a/.gitignore b/.gitignore index 758d4490..a840c57f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ node_modules npm-debug.log lib/generated/properties.js -lib/generated/propertyList.js +lib/generated/propertyDefinitions.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 56745080..c5d0ab9c 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -5,7 +5,7 @@ "use strict"; const generatedProperties = require("./generated/properties"); -const propertyList = require("./generated/propertyList"); +const propertyDefinitions = require("./generated/propertyDefinitions"); const { borderProperties, getPositionValue, @@ -194,7 +194,7 @@ class CSSStyleDeclaration { } catch { return; } - this._updating = undefined; + this._updating = false; if (typeof this._onChange === "function") { this._onChange(this.cssText); } @@ -270,7 +270,7 @@ class CSSStyleDeclaration { return; } const property = asciiLowercase(prop); - if (!propertyList.has(property)) { + if (!propertyDefinitions.has(property)) { return; } if (priority) { @@ -575,7 +575,7 @@ class CSSStyleDeclaration { Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties); // Additional properties -for (const definition of propertyList.values()) { +for (const definition of propertyDefinitions.values()) { const { legacyAliasOf, name, styleDeclaration } = definition; const property = legacyAliasOf ?? name; if (!Object.hasOwn(generatedProperties, property)) { diff --git a/lib/index.js b/lib/index.js index 7c7f2fd3..00d14b75 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,11 +1,11 @@ "use strict"; const { CSSStyleDeclaration } = require("./CSSStyleDeclaration"); -const propertyList = require("./generated/propertyList"); +const propertyDefinitions = require("./generated/propertyDefinitions"); const propertyNames = require("./generated/allProperties"); module.exports = { CSSStyleDeclaration, - propertyList: Object.fromEntries(propertyList), + propertyDefinitions: Object.fromEntries(propertyDefinitions), propertyNames: [...propertyNames] }; diff --git a/lib/normalize.js b/lib/normalize.js index 2078f0ba..1c30fa60 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -1,6 +1,6 @@ "use strict"; -const propertyList = require("./generated/propertyList"); +const propertyDefinitions = require("./generated/propertyDefinitions"); const { hasVarFunc, isGlobalKeyword, isValidPropertyValue, splitValue } = require("./parsers"); const background = require("./properties/background"); const border = require("./properties/border"); @@ -1227,7 +1227,7 @@ const prepareProperties = (properties, opt = {}) => { let hasPrecedingBackground = false; for (const [property, item] of properties) { const { value, priority } = item; - const { logicalPropertyGroup: shorthandProperty } = propertyList.get(property) ?? {}; + const { logicalPropertyGroup: shorthandProperty } = propertyDefinitions.get(property) ?? {}; if (borderProperties.has(property)) { borderProps.set(property, { property, value, priority }); } else if (shorthandProperties.has(shorthandProperty)) { diff --git a/package.json b/package.json index 2f2415f9..5812c500 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "keywords": [ "CSS", "CSSStyleDeclaration", - "CSSStyleProperties", "StyleSheet" ], "version": "5.3.3", @@ -62,7 +61,7 @@ "download": "node ./scripts/downloadLatestProperties.mjs && npm run generate", "generate": "run-p generate:*", "generate:properties": "node ./scripts/generateProperties.js", - "generate:propertyList": "node ./scripts/generatePropertyList.mjs", + "generate:propertyDefinitions": "node ./scripts/generatePropertyDefinitions.mjs", "lint": "npm run generate && eslint --max-warnings 0", "lint:fix": "eslint --fix --max-warnings 0", "prepublishOnly": "npm run test-ci", diff --git a/scripts/generatePropertyList.mjs b/scripts/generatePropertyDefinitions.mjs similarity index 87% rename from scripts/generatePropertyList.mjs rename to scripts/generatePropertyDefinitions.mjs index c5bfb02c..40c7cefc 100644 --- a/scripts/generatePropertyList.mjs +++ b/scripts/generatePropertyDefinitions.mjs @@ -29,19 +29,19 @@ if (Array.isArray(properties)) { } } -const propertyList = new Map(); +const propertyDefinitions = new Map(); for (const property of [...unifiedProperties].toSorted()) { const definition = definitions.get(property); if (definition) { - propertyList.set(property, definition); + propertyDefinitions.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)}); +module.exports = new Map(${JSON.stringify([...propertyDefinitions], null, 2)}); `; const { dirname } = import.meta; -const outputFile = path.resolve(dirname, "../lib/generated/propertyList.js"); +const outputFile = path.resolve(dirname, "../lib/generated/propertyDefinitions.js"); fs.writeFileSync(outputFile, output); diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 4914d005..17a3a659 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -3,7 +3,7 @@ const { describe, it } = require("node:test"); const assert = require("node:assert/strict"); const { CSSStyleDeclaration } = require("../lib/CSSStyleDeclaration"); -const propertyList = require("../lib/generated/propertyList"); +const propertyDefinitions = require("../lib/generated/propertyDefinitions"); const camelize = require("../scripts/camelize"); const window = { @@ -320,11 +320,11 @@ describe("properties", () => { assert.strictEqual(style instanceof CSSStyleDeclaration, true); }); - it("all dashed properties are included in propertyList", () => { + it("all dashed properties are included in propertyDefinitions", () => { const style = new CSSStyleDeclaration(window); for (const i in style) { if (/^[a-z]+(?:-[a-z]+)*$/.test(i)) { - assert.strictEqual(propertyList.has(i), true, i); + assert.strictEqual(propertyDefinitions.has(i), true, i); } } }); @@ -340,11 +340,11 @@ describe("properties", () => { }); // FIXME: https://github.com/jsdom/cssstyle/issues/210 - it.skip("all webkit prefixed properties are included in propertyList", () => { + it.skip("all webkit prefixed properties are included in propertyDefinitions", () => { const style = new CSSStyleDeclaration(window); for (const i in style) { if (/^-webkit-[a-z]+(?:-[a-z]+)*$/.test(i)) { - assert.strictEqual(propertyList.has(i), true, i); + assert.strictEqual(propertyDefinitions.has(i), true, i); } } }); From f95bfbd717cedb477129eb8e7f84f4ea26832fbb Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 13 Nov 2025 21:09:59 +0900 Subject: [PATCH 05/29] Remove setOptions for now revisit this when we address getComputedStyle(). --- lib/CSSStyleDeclaration.js | 27 --------------------- test/CSSStyleDeclaration.test.js | 40 -------------------------------- 2 files changed, 67 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index c5d0ab9c..e7694187 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -310,33 +310,6 @@ class CSSStyleDeclaration { return this._parentRule; } - /** - * Non-standard. - * To resolve getComputedStyle(), we need to setup additional option values. - * @param {object} opt - Options - * @returns {void} - */ - setOptions(opt = {}) { - for (const [key, value] of Object.entries(opt)) { - switch (key) { - case "format": { - if (value === "computedValue") { - this._computed = true; - this._options[key] = value; - } - break; - } - case "readOnly": { - this._readonly = value === true; - break; - } - default: { - this._options[key] = value; - } - } - } - } - // Internal methods /** * @param {string} property diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 17a3a659..b349eba0 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -272,46 +272,6 @@ describe("CSSStyleDeclaration", () => { style.setProperty("--foo", "green", "important"); assert.strictEqual(style.getPropertyPriority("--foo"), "important"); }); - - it("setProperty throws if read-only flag is set", () => { - const style = new CSSStyleDeclaration(window); - style.setProperty("--foo", "green"); - style.setOptions({ - readOnly: true - }); - assert.throws( - () => { - style.setProperty("--foo", "red"); - }, - (e) => { - assert.strictEqual(e instanceof window.DOMException, true); - assert.strictEqual(e.name, "NoModificationAllowedError"); - assert.strictEqual(e.message, "Property --foo can not be modified."); - return true; - } - ); - }); - - it("removeProperty throws if read-only flag is set", () => { - const style = new CSSStyleDeclaration(window); - style.setProperty("--foo", "green"); - style.setProperty("--bar", "red"); - assert.strictEqual(style.removeProperty("--foo"), "green"); - style.setOptions({ - readOnly: true - }); - assert.throws( - () => { - style.removeProperty("--bar"); - }, - (e) => { - 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("properties", () => { From 3d320cf25f907c59d64c573a074e04094a5759c2 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 13 Nov 2025 21:22:21 +0900 Subject: [PATCH 06/29] Delete implementedProperties.js --- lib/generated/implementedProperties.js | 313 ------------------------- 1 file changed, 313 deletions(-) delete mode 100644 lib/generated/implementedProperties.js diff --git a/lib/generated/implementedProperties.js b/lib/generated/implementedProperties.js deleted file mode 100644 index 59aaab1a..00000000 --- a/lib/generated/implementedProperties.js +++ /dev/null @@ -1,313 +0,0 @@ -"use strict"; -// autogenerated - 2025-11-01 - -module.exports = new Map([ - [ - "-webkit-border-after-color", - null - ], - [ - "-webkit-border-before-color", - null - ], - [ - "-webkit-border-end-color", - null - ], - [ - "-webkit-border-start-color", - null - ], - [ - "-webkit-column-rule-color", - null - ], - [ - "-webkit-tap-highlight-color", - null - ], - [ - "-webkit-text-emphasis-color", - null - ], - [ - "-webkit-text-fill-color", - null - ], - [ - "-webkit-text-stroke-color", - null - ], - [ - "background", - null - ], - [ - "background-attachment", - null - ], - [ - "background-clip", - null - ], - [ - "background-color", - null - ], - [ - "background-image", - null - ], - [ - "background-origin", - null - ], - [ - "background-position", - null - ], - [ - "background-repeat", - null - ], - [ - "background-size", - null - ], - [ - "border", - null - ], - [ - "border-bottom", - null - ], - [ - "border-bottom-color", - null - ], - [ - "border-bottom-style", - null - ], - [ - "border-bottom-width", - null - ], - [ - "border-collapse", - null - ], - [ - "border-color", - null - ], - [ - "border-left", - null - ], - [ - "border-left-color", - null - ], - [ - "border-left-style", - null - ], - [ - "border-left-width", - null - ], - [ - "border-right", - null - ], - [ - "border-right-color", - null - ], - [ - "border-right-style", - null - ], - [ - "border-right-width", - null - ], - [ - "border-spacing", - null - ], - [ - "border-style", - null - ], - [ - "border-top", - null - ], - [ - "border-top-color", - null - ], - [ - "border-top-style", - null - ], - [ - "border-top-width", - null - ], - [ - "border-width", - null - ], - [ - "bottom", - null - ], - [ - "clear", - null - ], - [ - "clip", - null - ], - [ - "color", - null - ], - [ - "display", - null - ], - [ - "flex", - null - ], - [ - "flex-basis", - null - ], - [ - "flex-grow", - null - ], - [ - "flex-shrink", - null - ], - [ - "float", - null - ], - [ - "flood-color", - null - ], - [ - "font", - null - ], - [ - "font-family", - null - ], - [ - "font-size", - null - ], - [ - "font-style", - null - ], - [ - "font-variant", - null - ], - [ - "font-weight", - null - ], - [ - "height", - null - ], - [ - "left", - null - ], - [ - "lighting-color", - null - ], - [ - "line-height", - null - ], - [ - "margin", - null - ], - [ - "margin-bottom", - null - ], - [ - "margin-left", - null - ], - [ - "margin-right", - null - ], - [ - "margin-top", - null - ], - [ - "opacity", - null - ], - [ - "outline-color", - null - ], - [ - "padding", - null - ], - [ - "padding-bottom", - null - ], - [ - "padding-left", - null - ], - [ - "padding-right", - null - ], - [ - "padding-top", - null - ], - [ - "right", - null - ], - [ - "stop-color", - null - ], - [ - "top", - null - ], - [ - "width", - null - ] -]); From de6afcbfa4c2e2f1e02c4baa60494f19efd51178 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 13 Nov 2025 22:09:38 +0900 Subject: [PATCH 07/29] Update allProperties.js --- lib/generated/allProperties.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/generated/allProperties.js b/lib/generated/allProperties.js index ac7b6aba..141be189 100644 --- a/lib/generated/allProperties.js +++ b/lib/generated/allProperties.js @@ -1,5 +1,5 @@ "use strict"; -// autogenerated - 2025-09-23 +// autogenerated - 2025-11-13 // https://www.w3.org/Style/CSS/all-properties.en.html module.exports = new Set([ @@ -461,6 +461,7 @@ module.exports = new Set([ "print-color-adjust", "quotes", "reading-flow", + "reading-order", "region-fragment", "resize", "rest", From 17f075ca9f4a4a59ce07165a7ba044f7fd5b4d43 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 13 Nov 2025 22:10:04 +0900 Subject: [PATCH 08/29] Fix legacy clip --- package-lock.json | 32 ++++++++++++++++---------------- package.json | 6 +++--- test/CSSStyleDeclaration.test.js | 8 ++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index d236837b..007b9130 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@asamuzakjp/css-color": "^4.0.5", - "@csstools/css-syntax-patches-for-csstree": "^1.0.15", + "@csstools/css-syntax-patches-for-csstree": "^1.0.16", "css-tree": "^3.1.0", "lru-cache": "^11.2.2" }, @@ -20,8 +20,8 @@ "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^8.0.0", - "eslint": "^9.39.0", + "@webref/css": "^8.0.1", + "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "globals": "^16.5.0", @@ -265,9 +265,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.15.tgz", - "integrity": "sha512-q0p6zkVq2lJnmzZVPR33doA51G7YOja+FBvRdp5ISIthL0MtFCgYHHhR563z9WFGxcOn0WfjSkPDJ5Qig3H3Sw==", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.16.tgz", + "integrity": "sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg==", "funding": [ { "type": "github", @@ -434,9 +434,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz", - "integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -603,9 +603,9 @@ "license": "MIT" }, "node_modules/@webref/css": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@webref/css/-/css-8.0.0.tgz", - "integrity": "sha512-1uQbu0veKzLNyT5xUmbNj9mjpXi/hcMSBmutrUEcX45sDQlcc3/f7CUPPxu0nKZbVJy2fB5/ZlZMNiH3rwPlyQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@webref/css/-/css-8.0.1.tgz", + "integrity": "sha512-TxryLLjDGJnNbv55arQ1v3YAzmuniXARJ5RaBLMIA1Ijx/uICUQ0GCSvGem997K4LsMW+UztrXPpxmsop9JcdA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -816,9 +816,9 @@ } }, "node_modules/eslint": { - "version": "9.39.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz", - "integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "peer": true, @@ -829,7 +829,7 @@ "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.0", + "@eslint/js": "9.39.1", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", diff --git a/package.json b/package.json index 5812c500..6671eebe 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "main": "./lib/index.js", "dependencies": { "@asamuzakjp/css-color": "^4.0.5", - "@csstools/css-syntax-patches-for-csstree": "^1.0.15", + "@csstools/css-syntax-patches-for-csstree": "^1.0.16", "css-tree": "^3.1.0", "lru-cache": "^11.2.2" }, @@ -48,8 +48,8 @@ "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^8.0.0", - "eslint": "^9.39.0", + "@webref/css": "^8.0.1", + "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", "globals": "^16.5.0", diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index b349eba0..87109384 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -560,10 +560,10 @@ describe("properties", () => { 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%)"); + style.clip = "rect(0, 3Em, 2pt, 5px)"; + assert.strictEqual(style.clip, "rect(0px, 3em, 2pt, 5px)"); assert.strictEqual(style.length, 2); - assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 50%);"); + assert.strictEqual(style.cssText, "clear: both; clip: rect(0px, 3em, 2pt, 5px);"); }); it("colors", () => { @@ -3821,7 +3821,7 @@ describe("masking", () => { }); it("clip should set / get legacy ", () => { - testPropertyValue("clip", "rect(0, 10px, 20%, 40EM)", "rect(0px, 10px, 20%, 40em)"); + testPropertyValue("clip", "rect(0, 10px, 2cm, 40EM)", "rect(0px, 10px, 2cm, 40em)"); }); }); From 468f39283bcc6663b9fe76d4a9a4139c0ef61035 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Fri, 14 Nov 2025 23:42:38 +0900 Subject: [PATCH 09/29] Update CSSStyleDeclaration.js --- lib/CSSStyleDeclaration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index e7694187..cd0c5709 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -39,6 +39,7 @@ class CSSStyleDeclaration { */ constructor(globalObject, opt = {}) { const { context, format, onChange } = opt; + // These help interface with jsdom. this._global = globalObject; this._onChange = onChange; @@ -65,7 +66,6 @@ class CSSStyleDeclaration { this._length = 0; // This is used internally by parsers. e.g. parsers.resolveCalc(), parsers.parseColor(), etc. - // Note that options may be updated later to resolve getComputedStyle(). See setOptions() below. this._options = { format: format === "computedValue" ? format : "specifiedValue" }; From b87215db13b1dd74a0221b6e7b9e4e7887d15d44 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 15 Nov 2025 12:21:15 +0900 Subject: [PATCH 10/29] Fix background shorthand handler --- lib/properties/background.js | 38 +++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/lib/properties/background.js b/lib/properties/background.js index 68251906..ce675c94 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -324,10 +324,42 @@ module.exports.definition = { ); } } + // No background-image if (l === 0) { - const [background] = bgMap.get("background-color"); - if (background) { - return background; + if (bgMap.size) { + const bgValue = []; + for (const [longhand, values] of bgMap) { + switch (longhand) { + case "background-color": { + const [value] = values; + if (parsers.hasVarFunc(value)) { + return ""; + } + bgValue.push(value); + break; + } + case "background-size": { + const [value] = values; + if (parsers.hasVarFunc(value)) { + return ""; + } + if (value && value !== module.exports.initialValues.get(longhand)) { + bgValue.push(`/ ${value}`); + } + break; + } + default: { + const [value] = values; + if (parsers.hasVarFunc(value)) { + return ""; + } + if (value && value !== module.exports.initialValues.get(longhand)) { + bgValue.push(value); + } + } + } + } + return bgValue.join(" "); } return ""; } From e2a20b13847e584ff086670bd432001b48a16086 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Tue, 18 Nov 2025 22:40:21 +0900 Subject: [PATCH 11/29] Reduce nest --- lib/normalize.js | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/lib/normalize.js b/lib/normalize.js index 1c30fa60..f68ba53a 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -192,13 +192,11 @@ const replaceBackgroundShorthand = (property, properties, opt) => { for (const bgValue of bgValues) { const bg = []; for (const [longhand, value] of Object.entries(bgValue)) { - if (value) { - if (value !== background.initialValues.get(longhand)) { - if (longhand === BACKGROUND_SIZE) { - bg.push(`/ ${value}`); - } else { - bg.push(value); - } + if (value && value !== background.initialValues.get(longhand)) { + if (longhand === BACKGROUND_SIZE) { + bg.push(`/ ${value}`); + } else { + bg.push(value); } } } @@ -1323,23 +1321,21 @@ const prepareProperties = (properties, opt = {}) => { if (prepareShorthands.size) { for (const [property, item] of prepareShorthands) { const shorthandItem = shorthandProperties.get(property); - if (item.size === shorthandItem.shorthandFor.size) { - if (shorthandItem.position) { - const positionValues = []; - let priority = ""; - for (const { value: longhandValue, priority: longhandPriority } of item.values()) { - positionValues.push(longhandValue); - if (longhandPriority) { - priority = longhandPriority; - } + if (item.size === shorthandItem.shorthandFor.size && shorthandItem.position) { + const positionValues = []; + let priority = ""; + for (const { value: longhandValue, priority: longhandPriority } of item.values()) { + positionValues.push(longhandValue); + if (longhandPriority) { + priority = longhandPriority; } - const value = getPositionValue(positionValues, shorthandItem.position); - parsedProperties.set(property, { - property, - value, - priority - }); } + const value = getPositionValue(positionValues, shorthandItem.position); + parsedProperties.set(property, { + property, + value, + priority + }); } } } From a0a667ebf4b3f965bca4bc79342630d7b3a7318f Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 10:05:28 +0900 Subject: [PATCH 12/29] Early return if value has already been set --- lib/CSSStyleDeclaration.js | 2 +- lib/properties/background.js | 4 ++++ lib/properties/backgroundAttachment.js | 4 ++++ lib/properties/backgroundClip.js | 4 ++++ lib/properties/backgroundColor.js | 4 ++++ lib/properties/backgroundImage.js | 4 ++++ lib/properties/backgroundOrigin.js | 4 ++++ lib/properties/backgroundPosition.js | 4 ++++ lib/properties/backgroundRepeat.js | 4 ++++ lib/properties/backgroundSize.js | 4 ++++ lib/properties/border.js | 4 ++++ lib/properties/borderBottom.js | 4 ++++ lib/properties/borderBottomColor.js | 4 ++++ lib/properties/borderBottomStyle.js | 4 ++++ lib/properties/borderBottomWidth.js | 4 ++++ lib/properties/borderCollapse.js | 4 ++++ lib/properties/borderColor.js | 4 ++++ lib/properties/borderLeft.js | 4 ++++ lib/properties/borderLeftColor.js | 4 ++++ lib/properties/borderLeftStyle.js | 4 ++++ lib/properties/borderLeftWidth.js | 4 ++++ lib/properties/borderRight.js | 4 ++++ lib/properties/borderRightColor.js | 4 ++++ lib/properties/borderRightStyle.js | 4 ++++ lib/properties/borderRightWidth.js | 4 ++++ lib/properties/borderSpacing.js | 4 ++++ lib/properties/borderStyle.js | 4 ++++ lib/properties/borderTop.js | 4 ++++ lib/properties/borderTopColor.js | 4 ++++ lib/properties/borderTopStyle.js | 4 ++++ lib/properties/borderTopWidth.js | 4 ++++ lib/properties/borderWidth.js | 4 ++++ lib/properties/bottom.js | 4 ++++ lib/properties/clear.js | 4 ++++ lib/properties/clip.js | 4 ++++ lib/properties/color.js | 4 ++++ lib/properties/display.js | 4 ++++ lib/properties/flex.js | 4 ++++ lib/properties/flexBasis.js | 4 ++++ lib/properties/flexGrow.js | 4 ++++ lib/properties/flexShrink.js | 4 ++++ lib/properties/float.js | 4 ++++ lib/properties/floodColor.js | 4 ++++ lib/properties/font.js | 4 ++++ lib/properties/fontFamily.js | 4 ++++ lib/properties/fontSize.js | 4 ++++ lib/properties/fontStyle.js | 4 ++++ lib/properties/fontVariant.js | 4 ++++ lib/properties/fontWeight.js | 4 ++++ lib/properties/height.js | 4 ++++ lib/properties/left.js | 4 ++++ lib/properties/lightingColor.js | 4 ++++ lib/properties/lineHeight.js | 4 ++++ lib/properties/margin.js | 4 ++++ lib/properties/marginBottom.js | 4 ++++ lib/properties/marginLeft.js | 4 ++++ lib/properties/marginRight.js | 4 ++++ lib/properties/marginTop.js | 4 ++++ lib/properties/opacity.js | 4 ++++ lib/properties/outlineColor.js | 4 ++++ lib/properties/padding.js | 4 ++++ lib/properties/paddingBottom.js | 4 ++++ lib/properties/paddingLeft.js | 4 ++++ lib/properties/paddingRight.js | 4 ++++ lib/properties/paddingTop.js | 4 ++++ lib/properties/right.js | 4 ++++ lib/properties/stopColor.js | 4 ++++ lib/properties/top.js | 4 ++++ lib/properties/webkitBorderAfterColor.js | 4 ++++ lib/properties/webkitBorderBeforeColor.js | 4 ++++ lib/properties/webkitBorderEndColor.js | 4 ++++ lib/properties/webkitBorderStartColor.js | 4 ++++ lib/properties/webkitColumnRuleColor.js | 4 ++++ lib/properties/webkitTapHighlightColor.js | 4 ++++ lib/properties/webkitTextEmphasisColor.js | 4 ++++ lib/properties/webkitTextFillColor.js | 4 ++++ lib/properties/webkitTextStrokeColor.js | 4 ++++ lib/properties/width.js | 4 ++++ lib/utils/propertyDescriptors.js | 4 ++++ 79 files changed, 313 insertions(+), 1 deletion(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index cd0c5709..23eff090 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -263,7 +263,7 @@ class CSSStyleDeclaration { this.removeProperty(prop); return; } - const priority = prior === "important" ? "important" : ""; + const priority = prior === "important" ? prior : ""; const isCustomProperty = prop.startsWith("--"); if (isCustomProperty) { this._setProperty(prop, value, priority); diff --git a/lib/properties/background.js b/lib/properties/background.js index ce675c94..8d2eb2d9 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -230,6 +230,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (v === "" || parsers.hasVarFunc(v)) { for (const [key] of module.exports.shorthandFor) { this._setProperty(key, ""); diff --git a/lib/properties/backgroundAttachment.js b/lib/properties/backgroundAttachment.js index 1d440e66..82f3a034 100644 --- a/lib/properties/backgroundAttachment.js +++ b/lib/properties/backgroundAttachment.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundClip.js b/lib/properties/backgroundClip.js index 40aec9e7..db220c3b 100644 --- a/lib/properties/backgroundClip.js +++ b/lib/properties/backgroundClip.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundColor.js b/lib/properties/backgroundColor.js index f06bd8b0..fe6576da 100644 --- a/lib/properties/backgroundColor.js +++ b/lib/properties/backgroundColor.js @@ -33,6 +33,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundImage.js b/lib/properties/backgroundImage.js index 2eb41b17..38c3d99c 100644 --- a/lib/properties/backgroundImage.js +++ b/lib/properties/backgroundImage.js @@ -56,6 +56,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundOrigin.js b/lib/properties/backgroundOrigin.js index 604b8d52..9eb96924 100644 --- a/lib/properties/backgroundOrigin.js +++ b/lib/properties/backgroundOrigin.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundPosition.js b/lib/properties/backgroundPosition.js index 96b24e2c..5978becc 100644 --- a/lib/properties/backgroundPosition.js +++ b/lib/properties/backgroundPosition.js @@ -181,6 +181,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundRepeat.js b/lib/properties/backgroundRepeat.js index 5440421f..1f742b06 100644 --- a/lib/properties/backgroundRepeat.js +++ b/lib/properties/backgroundRepeat.js @@ -67,6 +67,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/backgroundSize.js b/lib/properties/backgroundSize.js index ffb252ce..b720e938 100644 --- a/lib/properties/backgroundSize.js +++ b/lib/properties/backgroundSize.js @@ -98,6 +98,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/border.js b/lib/properties/border.js index c3544b81..289b0377 100644 --- a/lib/properties/border.js +++ b/lib/properties/border.js @@ -149,6 +149,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderBottom.js b/lib/properties/borderBottom.js index 381d85a7..943c4cc9 100644 --- a/lib/properties/borderBottom.js +++ b/lib/properties/borderBottom.js @@ -139,6 +139,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderBottomColor.js b/lib/properties/borderBottomColor.js index fc54da4c..58fd03cb 100644 --- a/lib/properties/borderBottomColor.js +++ b/lib/properties/borderBottomColor.js @@ -35,6 +35,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderBottomStyle.js b/lib/properties/borderBottomStyle.js index ad596756..4b9cd8d8 100644 --- a/lib/properties/borderBottomStyle.js +++ b/lib/properties/borderBottomStyle.js @@ -34,6 +34,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderBottomWidth.js b/lib/properties/borderBottomWidth.js index 4305290f..08c48d5c 100644 --- a/lib/properties/borderBottomWidth.js +++ b/lib/properties/borderBottomWidth.js @@ -48,6 +48,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderCollapse.js b/lib/properties/borderCollapse.js index c8f49856..0c37fd50 100644 --- a/lib/properties/borderCollapse.js +++ b/lib/properties/borderCollapse.js @@ -31,6 +31,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/borderColor.js b/lib/properties/borderColor.js index 6cb17df4..417e2370 100644 --- a/lib/properties/borderColor.js +++ b/lib/properties/borderColor.js @@ -95,6 +95,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderLeft.js b/lib/properties/borderLeft.js index ec0a8084..1ff7a7e0 100644 --- a/lib/properties/borderLeft.js +++ b/lib/properties/borderLeft.js @@ -139,6 +139,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderLeftColor.js b/lib/properties/borderLeftColor.js index 1f179628..35cc430d 100644 --- a/lib/properties/borderLeftColor.js +++ b/lib/properties/borderLeftColor.js @@ -35,6 +35,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderLeftStyle.js b/lib/properties/borderLeftStyle.js index d54928a9..1ed8bcfa 100644 --- a/lib/properties/borderLeftStyle.js +++ b/lib/properties/borderLeftStyle.js @@ -34,6 +34,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderLeftWidth.js b/lib/properties/borderLeftWidth.js index 72e3bb83..6fe9a74d 100644 --- a/lib/properties/borderLeftWidth.js +++ b/lib/properties/borderLeftWidth.js @@ -48,6 +48,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderRight.js b/lib/properties/borderRight.js index 5a00f45f..29268f14 100644 --- a/lib/properties/borderRight.js +++ b/lib/properties/borderRight.js @@ -139,6 +139,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderRightColor.js b/lib/properties/borderRightColor.js index 857ac14c..2adb3fc8 100644 --- a/lib/properties/borderRightColor.js +++ b/lib/properties/borderRightColor.js @@ -35,6 +35,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderRightStyle.js b/lib/properties/borderRightStyle.js index ea43a6e3..33ed47f7 100644 --- a/lib/properties/borderRightStyle.js +++ b/lib/properties/borderRightStyle.js @@ -34,6 +34,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderRightWidth.js b/lib/properties/borderRightWidth.js index beb95674..34893b4f 100644 --- a/lib/properties/borderRightWidth.js +++ b/lib/properties/borderRightWidth.js @@ -48,6 +48,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderSpacing.js b/lib/properties/borderSpacing.js index fee04259..438bc06d 100644 --- a/lib/properties/borderSpacing.js +++ b/lib/properties/borderSpacing.js @@ -43,6 +43,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/borderStyle.js b/lib/properties/borderStyle.js index c9c9f032..59fd3ace 100644 --- a/lib/properties/borderStyle.js +++ b/lib/properties/borderStyle.js @@ -95,6 +95,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderTop.js b/lib/properties/borderTop.js index f07d19bd..5883a653 100644 --- a/lib/properties/borderTop.js +++ b/lib/properties/borderTop.js @@ -139,6 +139,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderTopColor.js b/lib/properties/borderTopColor.js index be76ca59..a1f54879 100644 --- a/lib/properties/borderTopColor.js +++ b/lib/properties/borderTopColor.js @@ -35,6 +35,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderTopStyle.js b/lib/properties/borderTopStyle.js index 0134de2c..d2ce7a93 100644 --- a/lib/properties/borderTopStyle.js +++ b/lib/properties/borderTopStyle.js @@ -34,6 +34,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderTopWidth.js b/lib/properties/borderTopWidth.js index 74eeaf5d..f916afcd 100644 --- a/lib/properties/borderTopWidth.js +++ b/lib/properties/borderTopWidth.js @@ -48,6 +48,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/borderWidth.js b/lib/properties/borderWidth.js index 92f27dd4..9da553d6 100644 --- a/lib/properties/borderWidth.js +++ b/lib/properties/borderWidth.js @@ -112,6 +112,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._borderSetter(property, v, ""); } else { diff --git a/lib/properties/bottom.js b/lib/properties/bottom.js index dc1fd22d..4bbed837 100644 --- a/lib/properties/bottom.js +++ b/lib/properties/bottom.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/clear.js b/lib/properties/clear.js index aa7eb085..9eb093ba 100644 --- a/lib/properties/clear.js +++ b/lib/properties/clear.js @@ -31,6 +31,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/clip.js b/lib/properties/clip.js index afcb59f2..9769dbf1 100644 --- a/lib/properties/clip.js +++ b/lib/properties/clip.js @@ -56,6 +56,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/color.js b/lib/properties/color.js index 9b48b9f9..cf23c1d2 100644 --- a/lib/properties/color.js +++ b/lib/properties/color.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/display.js b/lib/properties/display.js index de06987f..efe0a105 100644 --- a/lib/properties/display.js +++ b/lib/properties/display.js @@ -188,6 +188,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/flex.js b/lib/properties/flex.js index dd484455..b43e6d9b 100644 --- a/lib/properties/flex.js +++ b/lib/properties/flex.js @@ -143,6 +143,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, ""); diff --git a/lib/properties/flexBasis.js b/lib/properties/flexBasis.js index b0bbd2dd..d1f20b83 100644 --- a/lib/properties/flexBasis.js +++ b/lib/properties/flexBasis.js @@ -40,6 +40,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/flexGrow.js b/lib/properties/flexGrow.js index e40d7b89..dea336fd 100644 --- a/lib/properties/flexGrow.js +++ b/lib/properties/flexGrow.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/flexShrink.js b/lib/properties/flexShrink.js index c4927189..e3963b6b 100644 --- a/lib/properties/flexShrink.js +++ b/lib/properties/flexShrink.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/float.js b/lib/properties/float.js index 6ffb6215..60afc6b6 100644 --- a/lib/properties/float.js +++ b/lib/properties/float.js @@ -31,6 +31,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/floodColor.js b/lib/properties/floodColor.js index 7d1ded01..68900d1b 100644 --- a/lib/properties/floodColor.js +++ b/lib/properties/floodColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/font.js b/lib/properties/font.js index 804832f1..1153b711 100644 --- a/lib/properties/font.js +++ b/lib/properties/font.js @@ -245,6 +245,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (v === "" || parsers.hasVarFunc(v)) { for (const [key] of module.exports.shorthandFor) { this._setProperty(key, ""); diff --git a/lib/properties/fontFamily.js b/lib/properties/fontFamily.js index 402cefae..29733e3b 100644 --- a/lib/properties/fontFamily.js +++ b/lib/properties/fontFamily.js @@ -72,6 +72,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/fontSize.js b/lib/properties/fontSize.js index bcef3e6a..bf7b74c8 100644 --- a/lib/properties/fontSize.js +++ b/lib/properties/fontSize.js @@ -46,6 +46,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/fontStyle.js b/lib/properties/fontStyle.js index 640afe90..99554be6 100644 --- a/lib/properties/fontStyle.js +++ b/lib/properties/fontStyle.js @@ -41,6 +41,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/fontVariant.js b/lib/properties/fontVariant.js index 4e230fa7..84258369 100644 --- a/lib/properties/fontVariant.js +++ b/lib/properties/fontVariant.js @@ -51,6 +51,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/fontWeight.js b/lib/properties/fontWeight.js index 0bb826b4..736ee1dd 100644 --- a/lib/properties/fontWeight.js +++ b/lib/properties/fontWeight.js @@ -50,6 +50,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/height.js b/lib/properties/height.js index 100e5ba6..cfcd2533 100644 --- a/lib/properties/height.js +++ b/lib/properties/height.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/left.js b/lib/properties/left.js index 5217e26c..3350977c 100644 --- a/lib/properties/left.js +++ b/lib/properties/left.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/lightingColor.js b/lib/properties/lightingColor.js index 21773f5b..0d7f1e4b 100644 --- a/lib/properties/lightingColor.js +++ b/lib/properties/lightingColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/lineHeight.js b/lib/properties/lineHeight.js index ee60c563..f7a75d1c 100644 --- a/lib/properties/lineHeight.js +++ b/lib/properties/lineHeight.js @@ -46,6 +46,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/margin.js b/lib/properties/margin.js index ca3c6f9b..6f87f87f 100644 --- a/lib/properties/margin.js +++ b/lib/properties/margin.js @@ -73,6 +73,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, ""); diff --git a/lib/properties/marginBottom.js b/lib/properties/marginBottom.js index f4cd4ddf..62f5fcdf 100644 --- a/lib/properties/marginBottom.js +++ b/lib/properties/marginBottom.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/marginLeft.js b/lib/properties/marginLeft.js index c38ed941..b66e7b11 100644 --- a/lib/properties/marginLeft.js +++ b/lib/properties/marginLeft.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/marginRight.js b/lib/properties/marginRight.js index 2e4a2174..6d1696c3 100644 --- a/lib/properties/marginRight.js +++ b/lib/properties/marginRight.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/marginTop.js b/lib/properties/marginTop.js index b16a9bee..59257856 100644 --- a/lib/properties/marginTop.js +++ b/lib/properties/marginTop.js @@ -42,6 +42,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/opacity.js b/lib/properties/opacity.js index c252c05a..6d9c6ed1 100644 --- a/lib/properties/opacity.js +++ b/lib/properties/opacity.js @@ -40,6 +40,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/outlineColor.js b/lib/properties/outlineColor.js index a6ed9680..761fad94 100644 --- a/lib/properties/outlineColor.js +++ b/lib/properties/outlineColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/padding.js b/lib/properties/padding.js index c3d3f3ee..77a83b22 100644 --- a/lib/properties/padding.js +++ b/lib/properties/padding.js @@ -75,6 +75,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, ""); diff --git a/lib/properties/paddingBottom.js b/lib/properties/paddingBottom.js index 89a6058e..f8f2f58a 100644 --- a/lib/properties/paddingBottom.js +++ b/lib/properties/paddingBottom.js @@ -47,6 +47,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/paddingLeft.js b/lib/properties/paddingLeft.js index 273fac3b..69217d08 100644 --- a/lib/properties/paddingLeft.js +++ b/lib/properties/paddingLeft.js @@ -47,6 +47,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/paddingRight.js b/lib/properties/paddingRight.js index f883f021..94de1d59 100644 --- a/lib/properties/paddingRight.js +++ b/lib/properties/paddingRight.js @@ -47,6 +47,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/paddingTop.js b/lib/properties/paddingTop.js index 7f44bf40..2f9d9976 100644 --- a/lib/properties/paddingTop.js +++ b/lib/properties/paddingTop.js @@ -47,6 +47,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(shorthand, ""); this._setProperty(property, v); diff --git a/lib/properties/right.js b/lib/properties/right.js index 38ece0fd..987ce751 100644 --- a/lib/properties/right.js +++ b/lib/properties/right.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/stopColor.js b/lib/properties/stopColor.js index b3fa2114..9208499d 100644 --- a/lib/properties/stopColor.js +++ b/lib/properties/stopColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/top.js b/lib/properties/top.js index e8af5342..de65de34 100644 --- a/lib/properties/top.js +++ b/lib/properties/top.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitBorderAfterColor.js b/lib/properties/webkitBorderAfterColor.js index ee0bc4fa..055196b1 100644 --- a/lib/properties/webkitBorderAfterColor.js +++ b/lib/properties/webkitBorderAfterColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitBorderBeforeColor.js b/lib/properties/webkitBorderBeforeColor.js index 6dc7f996..3991d63d 100644 --- a/lib/properties/webkitBorderBeforeColor.js +++ b/lib/properties/webkitBorderBeforeColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitBorderEndColor.js b/lib/properties/webkitBorderEndColor.js index fb58c297..717cc035 100644 --- a/lib/properties/webkitBorderEndColor.js +++ b/lib/properties/webkitBorderEndColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitBorderStartColor.js b/lib/properties/webkitBorderStartColor.js index e75c4a72..9fad8c05 100644 --- a/lib/properties/webkitBorderStartColor.js +++ b/lib/properties/webkitBorderStartColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitColumnRuleColor.js b/lib/properties/webkitColumnRuleColor.js index ec7e55da..9f46a0ef 100644 --- a/lib/properties/webkitColumnRuleColor.js +++ b/lib/properties/webkitColumnRuleColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitTapHighlightColor.js b/lib/properties/webkitTapHighlightColor.js index f5f97d0a..32a1f5d9 100644 --- a/lib/properties/webkitTapHighlightColor.js +++ b/lib/properties/webkitTapHighlightColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitTextEmphasisColor.js b/lib/properties/webkitTextEmphasisColor.js index ab31c6ef..a9c1a195 100644 --- a/lib/properties/webkitTextEmphasisColor.js +++ b/lib/properties/webkitTextEmphasisColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitTextFillColor.js b/lib/properties/webkitTextFillColor.js index dbb1aacd..856b2bc2 100644 --- a/lib/properties/webkitTextFillColor.js +++ b/lib/properties/webkitTextFillColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/webkitTextStrokeColor.js b/lib/properties/webkitTextStrokeColor.js index 93241044..b7b074a1 100644 --- a/lib/properties/webkitTextStrokeColor.js +++ b/lib/properties/webkitTextStrokeColor.js @@ -32,6 +32,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/properties/width.js b/lib/properties/width.js index 6b212563..e080c619 100644 --- a/lib/properties/width.js +++ b/lib/properties/width.js @@ -39,6 +39,10 @@ module.exports.parse = function parse(v, opt = {}) { module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { diff --git a/lib/utils/propertyDescriptors.js b/lib/utils/propertyDescriptors.js index 12612586..f2f3303f 100644 --- a/lib/utils/propertyDescriptors.js +++ b/lib/utils/propertyDescriptors.js @@ -7,6 +7,10 @@ exports.getPropertyDescriptor = function getPropertyDescriptor(property) { set(v) { const globalObject = this._global; v = parsers.prepareValue(v, globalObject); + // The value has already been set. + if (this._values.get(property) === v && !this._priorities.get(property)) { + return; + } if (v === "" || parsers.hasVarFunc(v)) { this._setProperty(property, v); } else { From 7b0083dad76aa2f1b67021cf7acb59133050f107 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 17:50:09 +0900 Subject: [PATCH 13/29] Update normalize.js --- lib/normalize.js | 2110 ++++++++++++++++++++++++---------------------- 1 file changed, 1112 insertions(+), 998 deletions(-) diff --git a/lib/normalize.js b/lib/normalize.js index f68ba53a..548af452 100644 --- a/lib/normalize.js +++ b/lib/normalize.js @@ -16,6 +16,7 @@ const font = require("./properties/font"); const margin = require("./properties/margin"); const padding = require("./properties/padding"); +/* constants */ const BACKGROUND = "background"; const BACKGROUND_COLOR = "background-color"; const BACKGROUND_SIZE = "background-size"; @@ -36,6 +37,12 @@ const WIDTH = "width"; const STYLE = "style"; const COLOR = "color"; const NONE = "none"; +const TRBL_INDICES = { + [TOP]: 0, + [RIGHT]: 1, + [BOTTOM]: 2, + [LEFT]: 3 +}; const shorthandProperties = new Map([ [BACKGROUND, background], @@ -75,103 +82,119 @@ const borderProperties = new Set([ ...borderLeft.shorthandFor.keys() ]); +const borderElements = { + name: BORDER, + positions: [TOP, RIGHT, BOTTOM, LEFT], + lines: [WIDTH, STYLE, COLOR] +}; + +/** + * Ensures consistent object shape (Hidden Class) for V8 optimization. + * + * @param {string} property - The property name. + * @param {string} [value=""] - The property value. + * @param {string} [priority=""] - The priority. + * @returns {Object} The property item object. + */ +const createPropertyItem = (property, value = "", priority = "") => ({ + property, + value, + priority +}); + +/** + * Retrieves a property item from the map or creates a default one if it doesn't exist. + * + * @param {string} property - The name of the property. + * @param {Map} properties - The map containing all properties. + * @returns {Object} The property item containing name, value, and priority. + */ +const getPropertyItem = (property, properties) => { + const propertyItem = properties.get(property) ?? createPropertyItem(property); + return propertyItem; +}; + +/** + * Calculates the value for a specific position (top, right, bottom, left) + * based on the array of values provided for a shorthand property. + * + * @param {string[]} positionValues - The values extracted from the shorthand property. + * @param {string} position - The specific position (top, right, bottom, left) to retrieve. + * @returns {string} The calculated value for the position. + */ const getPositionValue = (positionValues, position) => { - switch (positionValues.length) { - case 1: { - const [val1] = positionValues; - return val1; + const [val1, val2, val3, val4] = positionValues; + const index = TRBL_INDICES[position] ?? -1; + // If a specific position (top, right, bottom, left) is requested. + if (index !== -1) { + switch (positionValues.length) { + case 2: { + // Index 0 (Top) & 2 (Bottom) -> val1 + // Index 1 (Right) & 3 (Left) -> val2 + return index % 2 === 0 ? val1 : val2; + } + case 3: { + // Index 0 (Top) -> val1 + // Index 1 (Right) & 3 (Left) -> val2 + // Index 2 (Bottom) -> val3 + if (index === 2) { + return val3; + } + return index % 2 === 0 ? val1 : val2; + } + case 4: { + return positionValues[index]; + } + case 1: + default: { + return val1; + } } + } + // Fallback logic for when no specific position is requested. + switch (positionValues.length) { case 2: { - const [val1, val2] = positionValues; - switch (position) { - case TOP: { - return val1; - } - case RIGHT: { - return val2; - } - case BOTTOM: { - return val1; - } - case LEFT: { - return val2; - } - default: { - if (val1 === val2) { - return val1; - } - return `${val1} ${val2}`; - } + if (val1 === val2) { + return val1; } + return `${val1} ${val2}`; } case 3: { - const [val1, val2, val3] = positionValues; - switch (position) { - case TOP: { + if (val1 === val3) { + if (val1 === val2) { return val1; } - case RIGHT: { - return val2; - } - case BOTTOM: { - return val3; - } - case LEFT: { - return val2; - } - default: { - if (val1 === val3) { - if (val1 === val2) { - return val1; - } - return `${val1} ${val2}`; - } - return `${val1} ${val2} ${val3}`; - } + return `${val1} ${val2}`; } + return `${val1} ${val2} ${val3}`; } case 4: { - const [val1, val2, val3, val4] = positionValues; - switch (position) { - case TOP: { - return val1; - } - case RIGHT: { - return val2; - } - case BOTTOM: { - return val3; - } - case LEFT: { - return val4; - } - default: { - if (val2 === val4) { - if (val1 === val3) { - if (val1 === val2) { - return val1; - } - return `${val1} ${val2}`; - } - return `${val1} ${val2} ${val3}`; + if (val2 === val4) { + if (val1 === val3) { + if (val1 === val2) { + return val1; } - return `${val1} ${val2} ${val3} ${val4}`; + return `${val1} ${val2}`; } + return `${val1} ${val2} ${val3}`; } + return `${val1} ${val2} ${val3} ${val4}`; + } + case 1: + default: { + return val1; } - default: } }; -const getPropertyItem = (property, properties) => { - const propertyItem = properties.get(property) ?? { - property, - value: "", - priority: "" - }; - return propertyItem; -}; - +/** + * Replaces the background shorthand property based on individual longhand values. + * + * @param {string} property - The specific background longhand property being updated. + * @param {Map} properties - The map of all properties. + * @param {Object} opt - Parsing options including global object and configurations. + * @returns {string} The constructed background shorthand string. + */ const replaceBackgroundShorthand = (property, properties, opt) => { const { value: propertyValue } = properties.get(property); const parsedValue = background.shorthandFor.get(property).parse(propertyValue, opt); @@ -192,12 +215,13 @@ const replaceBackgroundShorthand = (property, properties, opt) => { for (const bgValue of bgValues) { const bg = []; for (const [longhand, value] of Object.entries(bgValue)) { - if (value && value !== background.initialValues.get(longhand)) { - if (longhand === BACKGROUND_SIZE) { - bg.push(`/ ${value}`); - } else { - bg.push(value); - } + if (!value || value === background.initialValues.get(longhand)) { + continue; + } + if (longhand === BACKGROUND_SIZE) { + bg.push(`/ ${value}`); + } else { + bg.push(value); } } backgrounds.push(bg.join(" ")); @@ -205,12 +229,15 @@ const replaceBackgroundShorthand = (property, properties, opt) => { return backgrounds.join(", "); }; -const borderElements = { - name: BORDER, - positions: [TOP, RIGHT, BOTTOM, LEFT], - lines: [WIDTH, STYLE, COLOR] -}; - +/** + * Checks if a property value matches the value within a border shorthand. + * + * @param {string} property - The property to check. + * @param {string} value - The value to compare. + * @param {string} shorthandValue - The shorthand string to parse and compare against. + * @param {Object} [opt={}] - Parsing options. + * @returns {boolean} True if the value matches the shorthand's value. + */ const matchesBorderShorthandValue = (property, value, shorthandValue, opt = {}) => { const { globalObject, options } = opt; const obj = border.parse(shorthandValue, { @@ -223,6 +250,14 @@ const matchesBorderShorthandValue = (property, value, shorthandValue, opt = {}) return value === border.initialValues.get(property); }; +/** + * Replaces or updates a value within a border shorthand string. + * + * @param {string} value - The new value to insert. + * @param {string} shorthandValue - The existing shorthand string. + * @param {Object} [opt={}] - Parsing options. + * @returns {string} The updated border shorthand string. + */ const replaceBorderShorthandValue = (value, shorthandValue, opt = {}) => { const { globalObject, options } = opt; const borderFirstInitialKey = border.initialValues.keys().next().value; @@ -265,642 +300,778 @@ const replaceBorderShorthandValue = (value, shorthandValue, opt = {}) => { return Object.values(shorthandObj).join(" "); }; +/** + * Replaces a value at a specific position (top, right, bottom, left) within a position shorthand. + * + * @param {string} value - The new value to set. + * @param {string[]} positionValues - The array of existing position values. + * @param {string} position - The position to update. + * @returns {string} The updated shorthand string. + */ const replacePositionValue = (value, positionValues, position) => { - switch (positionValues.length) { - case 1: { - const [val1] = positionValues; - if (val1 === value) { - return positionValues.join(" "); - } - switch (position) { - case TOP: { - return [value, val1, val1].join(" "); - } - case RIGHT: { - return [val1, value, val1, val1].join(" "); - } - case BOTTOM: { - return [val1, val1, value].join(" "); - } - case LEFT: { - return [val1, val1, val1, value].join(" "); - } - default: - } - break; - } - case 2: { - const [val1, val2] = positionValues; - if (val1 === val2) { - return replacePositionValue(value, [val1], position); - } - switch (position) { - case TOP: { - if (val1 === value) { - return positionValues.join(" "); + const index = TRBL_INDICES[position] ?? -1; + let currentValues = positionValues; + if (index !== -1) { + // Loop for reducing array length (instead of recursion) + while (true) { + const [val1, val2, val3, val4] = currentValues; + switch (currentValues.length) { + case 2: { + if (val1 === val2) { + currentValues = [val1]; + continue; + } + switch (index) { + // Top + case 0: { + if (val1 === value) { + return currentValues.join(" "); + } + return `${value} ${val2} ${val1}`; + } + // Right + case 1: { + if (val2 === value) { + return currentValues.join(" "); + } + return `${val1} ${value} ${val1} ${val2}`; + } + // Bottom + case 2: { + if (val1 === value) { + return currentValues.join(" "); + } + return `${val1} ${val2} ${value}`; + } + // Left + case 3: + default: { + if (val2 === value) { + return currentValues.join(" "); + } + return `${val1} ${val2} ${val1} ${value}`; + } } - return [value, val2, val1].join(" "); } - case RIGHT: { - if (val2 === value) { - return positionValues.join(" "); + case 3: { + if (val1 === val3) { + currentValues = [val1, val2]; + continue; + } + switch (index) { + // Top + case 0: { + if (val1 === value) { + return currentValues.join(" "); + } else if (val3 === value) { + return `${value} ${val2}`; + } + return `${value} ${val2} ${val3}`; + } + // Right + case 1: { + if (val2 === value) { + return currentValues.join(" "); + } + return `${val1} ${value} ${val3} ${val2}`; + } + // Bottom + case 2: { + if (val3 === value) { + return currentValues.join(" "); + } else if (val1 === value) { + return `${val1} ${val2}`; + } + return `${val1} ${val2} ${value}`; + } + // Left + case 3: + default: { + if (val2 === value) { + return currentValues.join(" "); + } + return `${val1} ${val2} ${val3} ${value}`; + } } - return [val1, value, val1, val2].join(" "); } - case BOTTOM: { - if (val1 === value) { - return positionValues.join(" "); + case 4: { + if (val2 === val4) { + currentValues = [val1, val2, val3]; + continue; + } + switch (index) { + // Top + case 0: { + if (val1 === value) { + return currentValues.join(" "); + } + return `${value} ${val2} ${val3} ${val4}`; + } + // Right + case 1: { + if (val2 === value) { + return currentValues.join(" "); + } else if (val4 === value) { + return `${val1} ${value} ${val3}`; + } + return `${val1} ${value} ${val3} ${val4}`; + } + // Bottom + case 2: { + if (val3 === value) { + return currentValues.join(" "); + } + return `${val1} ${val2} ${value} ${val4}`; + } + // Left + case 3: + default: { + if (val4 === value) { + return currentValues.join(" "); + } else if (val2 === value) { + return `${val1} ${val2} ${val3}`; + } + return `${val1} ${val2} ${val3} ${value}`; + } } - return [val1, val2, value].join(" "); } - case LEFT: { - if (val2 === value) { - return positionValues.join(" "); + case 1: + default: { + const [val] = currentValues; + if (val === value) { + return currentValues.join(" "); + } + switch (index) { + // Top + case 0: { + return `${value} ${val} ${val}`; + } + // Right + case 1: { + return `${val} ${value} ${val} ${val}`; + } + // Bottom + case 2: { + return `${val} ${val} ${value}`; + } + // Left + case 3: + default: { + return `${val} ${val} ${val} ${value}`; + } } - return [val1, val2, val1, value].join(" "); } - default: } - break; + } + } + // Fallback logic for when no specific position is requested. + const [val1, val2, val3, val4] = currentValues; + switch (currentValues.length) { + case 2: { + if (val1 === val2) { + return val1; + } + return `${val1} ${val2}`; } case 3: { - const [val1, val2, val3] = positionValues; if (val1 === val3) { - return replacePositionValue(value, [val1, val2], position); - } - switch (position) { - case TOP: { - if (val1 === value) { - return positionValues.join(" "); - } else if (val3 === value) { - return [value, val2].join(" "); - } - return [value, val2, val3].join(" "); - } - case RIGHT: { - if (val2 === value) { - return positionValues.join(" "); - } - return [val1, value, val3, val2].join(" "); - } - case BOTTOM: { - if (val3 === value) { - return positionValues.join(" "); - } else if (val1 === value) { - return [val1, val2].join(" "); - } - return [val1, val2, value].join(" "); - } - case LEFT: { - if (val2 === value) { - return positionValues.join(" "); - } - return [val1, val2, val3, value].join(" "); + if (val1 === val2) { + return val1; } - default: + return `${val1} ${val2}`; } - break; + return `${val1} ${val2} ${val3}`; } case 4: { - const [val1, val2, val3, val4] = positionValues; if (val2 === val4) { - return replacePositionValue(value, [val1, val2, val3], position); - } - switch (position) { - case TOP: { - if (val1 === value) { - return positionValues.join(" "); - } - return [value, val2, val3, val4].join(" "); - } - case RIGHT: { - if (val2 === value) { - return positionValues.join(" "); - } else if (val4 === value) { - return [val1, value, val3].join(" "); - } - return [val1, value, val3, val4].join(" "); - } - case BOTTOM: { - if (val3 === value) { - return positionValues.join(" "); - } - return [val1, val2, value, val4].join(" "); - } - case LEFT: { - if (val4 === value) { - return positionValues.join(" "); - } else if (val2 === value) { - return [val1, val2, val3].join(" "); + if (val1 === val3) { + if (val1 === val2) { + return val1; } - return [val1, val2, val3, value].join(" "); + return `${val1} ${val2}`; } - default: + return `${val1} ${val2} ${val3}`; } - break; + return `${val1} ${val2} ${val3} ${val4}`; + } + case 1: + default: { + return val1; } - default: } }; -const prepareBorderProperties = (property, value, priority, properties, opt = {}) => { - if (typeof property !== "string" || value === null) { - return; - } +/** + * Handles border property preparation when the value is a string. + * + * @param {Object} params - The parameters object. + * @param {string} params.property - The property name. + * @param {string} params.value - The property value. + * @param {string} params.priority - The property priority. + * @param {Map} params.properties - The map of properties. + * @param {Object} params.parts - The split property name parts. + * @param {Object} params.opt - Parsing options. + * @param {Map} params.borderItems - The map to store processed border items. + */ +const prepareBorderStringValue = ({ + property, + value, + priority, + properties, + parts, + opt, + borderItems +}) => { + const { prop1, prop2, prop3 } = parts; const { globalObject, options } = opt; const parseOpt = { globalObject, options }; - const { lines, name, positions } = borderElements; - const [prop1, prop2, prop3] = property.split("-"); - if (prop1 !== name) { - return; - } else if (positions.includes(prop2)) { - if (prop3) { - if (!lines.includes(prop3)) { - return; + const { lines, positions, name: nameProperty } = borderElements; + const nameItem = getPropertyItem(nameProperty, properties); + const imageItem = getPropertyItem(BORDER_IMAGE, properties); + // Handle longhand properties. + if (prop3) { + const lineProperty = `${prop1}-${prop3}`; + const lineItem = getPropertyItem(lineProperty, properties); + const positionProperty = `${prop1}-${prop2}`; + const positionItem = getPropertyItem(positionProperty, properties); + const longhandProperty = `${prop1}-${prop2}-${prop3}`; + const longhandItem = getPropertyItem(longhandProperty, properties); + longhandItem.value = value; + longhandItem.priority = priority; + const propertyValue = hasVarFunc(value) ? "" : value; + if (propertyValue === "") { + nameItem.value = ""; + lineItem.value = ""; + positionItem.value = ""; + } else if (isGlobalKeyword(propertyValue)) { + if (nameItem.value !== propertyValue) { + nameItem.value = ""; + } + if (lineItem.value !== propertyValue) { + lineItem.value = ""; + } + if (positionItem.value !== propertyValue) { + positionItem.value = ""; + } + } else { + if ( + nameItem.value && + !matchesBorderShorthandValue(lineProperty, propertyValue, nameItem.value, parseOpt) + ) { + nameItem.value = ""; + } + if (lineItem.value) { + lineItem.value = replacePositionValue(propertyValue, splitValue(lineItem.value), prop2); + } + if ( + positionItem.value && + !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, parseOpt) + ) { + positionItem.value = ""; } } - } else if (lines.includes(prop2)) { - if (prop3) { - return; + borderItems.set(nameProperty, nameItem); + borderItems.set(BORDER_IMAGE, imageItem); + borderItems.set(lineProperty, lineItem); + borderItems.set(positionProperty, positionItem); + borderItems.set(longhandProperty, longhandItem); + // Handle side-specific border shorthands (border-top, border-right, border-bottom, border-left). + } else if (prop2 && positions.includes(prop2)) { + const lineWidthProperty = `${prop1}-width`; + const lineWidthItem = getPropertyItem(lineWidthProperty, properties); + const lineStyleProperty = `${prop1}-style`; + const lineStyleItem = getPropertyItem(lineStyleProperty, properties); + const lineColorProperty = `${prop1}-color`; + const lineColorItem = getPropertyItem(lineColorProperty, properties); + const positionProperty = `${prop1}-${prop2}`; + const positionItem = getPropertyItem(positionProperty, properties); + positionItem.value = value; + positionItem.priority = priority; + const propertyValue = hasVarFunc(value) ? "" : value; + if (propertyValue === "") { + nameItem.value = ""; + lineWidthItem.value = ""; + lineStyleItem.value = ""; + lineColorItem.value = ""; + } else if (isGlobalKeyword(propertyValue)) { + if (nameItem.value !== propertyValue) { + nameItem.value = ""; + } + if (lineWidthItem.value !== propertyValue) { + lineWidthItem.value = ""; + } + if (lineStyleItem.value !== propertyValue) { + lineStyleItem.value = ""; + } + if (lineColorItem.value !== propertyValue) { + lineColorItem.value = ""; + } + } else { + if ( + nameItem.value && + !matchesBorderShorthandValue(property, propertyValue, nameItem.value, parseOpt) + ) { + nameItem.value = ""; + } + if ( + lineWidthItem.value && + isValidPropertyValue(lineWidthProperty, propertyValue, globalObject) + ) { + lineWidthItem.value = propertyValue; + } + if ( + lineStyleItem.value && + isValidPropertyValue(lineStyleProperty, propertyValue, globalObject) + ) { + lineStyleItem.value = propertyValue; + } + if ( + lineColorItem.value && + isValidPropertyValue(lineColorProperty, propertyValue, globalObject) + ) { + lineColorItem.value = propertyValue; + } } - } - const borderItems = new Map(); - const nameProperty = prop1; - // Empty string, global keywords, var(), value of longhands. - if (typeof value === "string") { - // Handle longhand properties - if (prop3) { - const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(BORDER_IMAGE, properties); - const lineProperty = `${prop1}-${prop3}`; - const lineItem = getPropertyItem(lineProperty, properties); - const positionProperty = `${prop1}-${prop2}`; - const positionItem = getPropertyItem(positionProperty, properties); - const longhandProperty = `${prop1}-${prop2}-${prop3}`; + for (const line of lines) { + const longhandProperty = `${prop1}-${prop2}-${line}`; const longhandItem = getPropertyItem(longhandProperty, properties); - longhandItem.value = value; + longhandItem.value = propertyValue; longhandItem.priority = priority; - const propertyValue = hasVarFunc(value) ? "" : value; - if (propertyValue === "") { + borderItems.set(longhandProperty, longhandItem); + } + borderItems.set(nameProperty, nameItem); + borderItems.set(BORDER_IMAGE, imageItem); + borderItems.set(lineWidthProperty, lineWidthItem); + borderItems.set(lineStyleProperty, lineStyleItem); + borderItems.set(lineColorProperty, lineColorItem); + borderItems.set(positionProperty, positionItem); + // Handle property-specific border shorthands (border-width, border-style, border-color). + } else if (prop2 && lines.includes(prop2)) { + const lineProperty = `${prop1}-${prop2}`; + const lineItem = getPropertyItem(lineProperty, properties); + lineItem.value = value; + lineItem.priority = priority; + const propertyValue = hasVarFunc(value) ? "" : value; + if (propertyValue === "") { + nameItem.value = ""; + } else if (isGlobalKeyword(propertyValue)) { + if (nameItem.value !== propertyValue) { nameItem.value = ""; - lineItem.value = ""; - positionItem.value = ""; - } else if (isGlobalKeyword(propertyValue)) { - if (nameItem.value !== propertyValue) { - nameItem.value = ""; - } - if (lineItem.value !== propertyValue) { - lineItem.value = ""; - } - if (positionItem.value !== propertyValue) { - positionItem.value = ""; - } + } + } + for (const position of positions) { + const positionProperty = `${prop1}-${position}`; + const positionItem = getPropertyItem(positionProperty, properties); + const longhandProperty = `${prop1}-${position}-${prop2}`; + const longhandItem = getPropertyItem(longhandProperty, properties); + if (propertyValue) { + positionItem.value = replaceBorderShorthandValue( + propertyValue, + positionItem.value, + parseOpt + ); } else { - if ( - nameItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, nameItem.value, parseOpt) - ) { - nameItem.value = ""; - } - if (lineItem.value) { - lineItem.value = replacePositionValue(propertyValue, splitValue(lineItem.value), prop2); - } - if ( - positionItem.value && - !matchesBorderShorthandValue(lineProperty, propertyValue, positionItem.value, parseOpt) - ) { - positionItem.value = ""; - } + positionItem.value = ""; } - borderItems.set(nameProperty, nameItem); - borderItems.set(BORDER_IMAGE, imageItem); - borderItems.set(lineProperty, lineItem); + longhandItem.value = propertyValue; + longhandItem.priority = priority; borderItems.set(positionProperty, positionItem); borderItems.set(longhandProperty, longhandItem); - // 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); - const lineWidthProperty = `${prop1}-width`; - const lineWidthItem = getPropertyItem(lineWidthProperty, properties); - const lineStyleProperty = `${prop1}-style`; - const lineStyleItem = getPropertyItem(lineStyleProperty, properties); - const lineColorProperty = `${prop1}-color`; - const lineColorItem = getPropertyItem(lineColorProperty, properties); - const positionProperty = `${prop1}-${prop2}`; + } + borderItems.set(nameProperty, nameItem); + borderItems.set(BORDER_IMAGE, imageItem); + borderItems.set(lineProperty, lineItem); + // Handle border shorthand. + } else { + const propertyValue = hasVarFunc(value) ? "" : value; + imageItem.value = propertyValue ? NONE : ""; + for (const line of lines) { + const lineProperty = `${prop1}-${line}`; + const lineItem = getPropertyItem(lineProperty, properties); + lineItem.value = propertyValue; + lineItem.priority = priority; + borderItems.set(lineProperty, lineItem); + } + for (const position of positions) { + const positionProperty = `${prop1}-${position}`; const positionItem = getPropertyItem(positionProperty, properties); - positionItem.value = value; + positionItem.value = propertyValue; positionItem.priority = priority; - const propertyValue = hasVarFunc(value) ? "" : value; - if (propertyValue === "") { - nameItem.value = ""; - lineWidthItem.value = ""; - lineStyleItem.value = ""; - lineColorItem.value = ""; - } else if (isGlobalKeyword(propertyValue)) { - if (nameItem.value !== propertyValue) { - nameItem.value = ""; - } - if (lineWidthItem.value !== propertyValue) { - lineWidthItem.value = ""; - } - if (lineStyleItem.value !== propertyValue) { - lineStyleItem.value = ""; - } - if (lineColorItem.value !== propertyValue) { - lineColorItem.value = ""; - } - } else { - if ( - nameItem.value && - !matchesBorderShorthandValue(property, propertyValue, nameItem.value, parseOpt) - ) { - nameItem.value = ""; - } - if ( - lineWidthItem.value && - isValidPropertyValue(lineWidthProperty, propertyValue, globalObject) - ) { - lineWidthItem.value = propertyValue; - } - if ( - lineStyleItem.value && - isValidPropertyValue(lineStyleProperty, propertyValue, globalObject) - ) { - lineStyleItem.value = propertyValue; - } - if ( - lineColorItem.value && - isValidPropertyValue(lineColorProperty, propertyValue, globalObject) - ) { - lineColorItem.value = propertyValue; - } - } + borderItems.set(positionProperty, positionItem); for (const line of lines) { - const longhandProperty = `${prop1}-${prop2}-${line}`; + const longhandProperty = `${positionProperty}-${line}`; const longhandItem = getPropertyItem(longhandProperty, properties); longhandItem.value = propertyValue; longhandItem.priority = priority; borderItems.set(longhandProperty, longhandItem); } - borderItems.set(nameProperty, nameItem); - borderItems.set(BORDER_IMAGE, imageItem); - borderItems.set(lineWidthProperty, lineWidthItem); - borderItems.set(lineStyleProperty, lineStyleItem); - borderItems.set(lineColorProperty, lineColorItem); - borderItems.set(positionProperty, positionItem); - // 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); - const lineProperty = `${prop1}-${prop2}`; - const lineItem = getPropertyItem(lineProperty, properties); - lineItem.value = value; - lineItem.priority = priority; - const propertyValue = hasVarFunc(value) ? "" : value; - if (propertyValue === "") { + } + borderItems.set(property, nameItem); + borderItems.set(BORDER_IMAGE, imageItem); + } +}; + +/** + * Handles border property preparation when the value is an array. + * + * @param {Object} params - The parameters object. + * @param {Array} params.value - The property value. + * @param {string} params.priority - The property priority. + * @param {Map} params.properties - The map of properties. + * @param {Object} params.parts - The split property name parts. + * @param {Object} params.opt - Parsing options. + * @param {Map} params.borderItems - The map to store processed border items. + */ +const prepareBorderArrayValue = ({ value, priority, properties, parts, opt, borderItems }) => { + const { prop1, prop2 } = parts; + const { globalObject, options } = opt; + const parseOpt = { + globalObject, + options + }; + const { lines, positions, name: nameProperty } = borderElements; + if (!value.length || !lines.includes(prop2)) { + return; + } + const nameItem = getPropertyItem(nameProperty, 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) { + if (hasVarFunc(nameItem.value)) { nameItem.value = ""; - } else if (isGlobalKeyword(propertyValue)) { - if (nameItem.value !== propertyValue) { - nameItem.value = ""; - } - } - for (const position of positions) { - const positionProperty = `${prop1}-${position}`; - const positionItem = getPropertyItem(positionProperty, properties); - const longhandProperty = `${prop1}-${position}-${prop2}`; - const longhandItem = getPropertyItem(longhandProperty, properties); - if (propertyValue) { - positionItem.value = replaceBorderShorthandValue( - propertyValue, - positionItem.value, - parseOpt - ); - } else { - positionItem.value = ""; - } - longhandItem.value = propertyValue; - longhandItem.priority = priority; - borderItems.set(positionProperty, positionItem); - borderItems.set(longhandProperty, longhandItem); - } - borderItems.set(nameProperty, nameItem); - borderItems.set(BORDER_IMAGE, imageItem); - borderItems.set(lineProperty, lineItem); - // Handle border shorthand - } else { - const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(BORDER_IMAGE, properties); - const propertyValue = hasVarFunc(value) ? "" : value; - imageItem.value = propertyValue ? NONE : ""; - for (const line of lines) { - const lineProperty = `${prop1}-${line}`; - const lineItem = getPropertyItem(lineProperty, properties); - lineItem.value = propertyValue; - lineItem.priority = priority; - borderItems.set(lineProperty, lineItem); - } - for (const position of positions) { - const positionProperty = `${prop1}-${position}`; - const positionItem = getPropertyItem(positionProperty, properties); - positionItem.value = propertyValue; - positionItem.priority = priority; - borderItems.set(positionProperty, positionItem); - for (const line of lines) { - const longhandProperty = `${positionProperty}-${line}`; - const longhandItem = getPropertyItem(longhandProperty, properties); - longhandItem.value = propertyValue; - longhandItem.priority = priority; - borderItems.set(longhandProperty, longhandItem); - } + } else if (propertyValue) { + nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, parseOpt); } - borderItems.set(property, nameItem); - borderItems.set(BORDER_IMAGE, imageItem); } - // Values of border-width, border-style, border-color - } else if (Array.isArray(value)) { - if (!value.length || !lines.includes(prop2)) { + } else { + nameItem.value = ""; + } + lineItem.value = value.join(" "); + lineItem.priority = priority; + const positionValues = {}; + const [val1, val2, val3, val4] = value; + switch (value.length) { + case 2: { + positionValues.top = val1; + positionValues.right = val2; + positionValues.bottom = val1; + positionValues.left = val2; + break; + } + case 3: { + positionValues.top = val1; + positionValues.right = val2; + positionValues.bottom = val3; + positionValues.left = val2; + break; + } + case 4: { + positionValues.top = val1; + positionValues.right = val2; + positionValues.bottom = val3; + positionValues.left = val4; + break; + } + case 1: + default: { + positionValues.top = val1; + positionValues.right = val1; + positionValues.bottom = val1; + positionValues.left = val1; + } + } + for (const position of positions) { + const positionProperty = `${prop1}-${position}`; + const positionItem = getPropertyItem(positionProperty, properties); + if (positionItem.value && positionValues[position]) { + positionItem.value = replaceBorderShorthandValue( + positionValues[position], + positionItem.value, + parseOpt + ); + } + const longhandProperty = `${positionProperty}-${prop2}`; + const longhandItem = getPropertyItem(longhandProperty, properties); + longhandItem.value = positionValues[position]; + longhandItem.priority = priority; + borderItems.set(positionProperty, positionItem); + borderItems.set(longhandProperty, longhandItem); + } + borderItems.set(nameProperty, nameItem); + borderItems.set(BORDER_IMAGE, imageItem); + borderItems.set(lineProperty, lineItem); +}; + +/** + * Handles border property preparation when the value is an object. + * + * @param {Object} params - The parameters object. + * @param {string} params.property - The property name. + * @param {Object} params.value - The property value. + * @param {string} params.priority - The property priority. + * @param {Map} params.properties - The map of properties. + * @param {Object} params.parts - The split property name parts. + * @param {Object} params.opt - Parsing options. + * @param {Map} params.borderItems - The map to store processed border items. + */ +const prepareBorderObjectValue = ({ + property, + value, + priority, + properties, + parts, + opt, + borderItems +}) => { + const { prop1, prop2 } = parts; + const { globalObject, options } = opt; + const parseOpt = { + globalObject, + options + }; + const { lines, positions, name: nameProperty } = borderElements; + const imageItem = getPropertyItem(BORDER_IMAGE, properties); + // Handle position shorthands. + if (prop2) { + if (!positions.includes(prop2)) { return; } const nameItem = getPropertyItem(nameProperty, 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) { - if (hasVarFunc(nameItem.value)) { + const lineWidthProperty = `${prop1}-width`; + const lineWidthItem = getPropertyItem(lineWidthProperty, properties); + const lineStyleProperty = `${prop1}-style`; + const lineStyleItem = getPropertyItem(lineStyleProperty, properties); + const lineColorProperty = `${prop1}-color`; + const lineColorItem = getPropertyItem(lineColorProperty, properties); + const positionProperty = `${prop1}-${prop2}`; + const positionItem = getPropertyItem(positionProperty, properties); + if (nameItem.value) { + for (const positionValue of Object.values(value)) { + if (!matchesBorderShorthandValue(property, positionValue, nameItem.value, parseOpt)) { nameItem.value = ""; - } else if (propertyValue) { - nameItem.value = replaceBorderShorthandValue(propertyValue, nameItem.value, parseOpt); + break; } } - } else { - nameItem.value = ""; - } - lineItem.value = value.join(" "); - lineItem.priority = priority; - const positionValues = {}; - switch (value.length) { - case 1: { - const [val1] = value; - positionValues.top = val1; - positionValues.right = val1; - positionValues.bottom = val1; - positionValues.left = val1; - break; - } - case 2: { - const [val1, val2] = value; - positionValues.top = val1; - positionValues.right = val2; - positionValues.bottom = val1; - positionValues.left = val2; - break; - } - case 3: { - const [val1, val2, val3] = value; - positionValues.top = val1; - positionValues.right = val2; - positionValues.bottom = val3; - positionValues.left = val2; - break; - } - case 4: { - const [val1, val2, val3, val4] = value; - positionValues.top = val1; - positionValues.right = val2; - positionValues.bottom = val3; - positionValues.left = val4; - break; - } - default: { - return; - } } - for (const position of positions) { - const positionProperty = `${prop1}-${position}`; - const positionItem = getPropertyItem(positionProperty, properties); - if (positionItem.value && positionValues[position]) { - positionItem.value = replaceBorderShorthandValue( - positionValues[position], - positionItem.value, - parseOpt + positionItem.value = Object.values(value).join(" "); + positionItem.priority = priority; + for (const line of lines) { + const longhandProperty = `${prop1}-${prop2}-${line}`; + const longhandItem = getPropertyItem(longhandProperty, properties); + const itemValue = Object.hasOwn(value, longhandProperty) + ? value[longhandProperty] + : border.initialValues.get(`${prop1}-${line}`); + if (line === WIDTH && lineWidthItem.value) { + lineWidthItem.value = replacePositionValue( + itemValue, + splitValue(lineWidthItem.value), + prop2 + ); + } else if (line === STYLE && lineStyleItem.value) { + lineStyleItem.value = replacePositionValue( + itemValue, + splitValue(lineStyleItem.value), + prop2 + ); + } else if (line === COLOR && lineColorItem.value) { + lineColorItem.value = replacePositionValue( + itemValue, + splitValue(lineColorItem.value), + prop2 ); } - const longhandProperty = `${positionProperty}-${prop2}`; - const longhandItem = getPropertyItem(longhandProperty, properties); - longhandItem.value = positionValues[position]; + longhandItem.value = itemValue; longhandItem.priority = priority; - borderItems.set(positionProperty, positionItem); borderItems.set(longhandProperty, longhandItem); } borderItems.set(nameProperty, nameItem); 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") { - // Handle position shorthands - if (prop2) { - if (!positions.includes(prop2)) { - return; - } - const nameItem = getPropertyItem(nameProperty, properties); - const imageItem = getPropertyItem(BORDER_IMAGE, properties); - const lineWidthProperty = `${prop1}-width`; - const lineWidthItem = getPropertyItem(lineWidthProperty, properties); - const lineStyleProperty = `${prop1}-style`; - const lineStyleItem = getPropertyItem(lineStyleProperty, properties); - const lineColorProperty = `${prop1}-color`; - const lineColorItem = getPropertyItem(lineColorProperty, properties); - const positionProperty = `${prop1}-${prop2}`; + borderItems.set(lineWidthProperty, lineWidthItem); + borderItems.set(lineStyleProperty, lineStyleItem); + borderItems.set(lineColorProperty, lineColorItem); + borderItems.set(positionProperty, positionItem); + // Handle border shorthand. + } else { + const nameItem = getPropertyItem(prop1, properties); + const lineWidthProperty = `${prop1}-width`; + const lineWidthItem = getPropertyItem(lineWidthProperty, properties); + const lineStyleProperty = `${prop1}-style`; + const lineStyleItem = getPropertyItem(lineStyleProperty, properties); + const lineColorProperty = `${prop1}-color`; + const lineColorItem = getPropertyItem(lineColorProperty, properties); + const propertyValue = Object.values(value).join(" "); + nameItem.value = propertyValue; + nameItem.priority = priority; + imageItem.value = propertyValue ? NONE : ""; + if (Object.hasOwn(value, lineWidthProperty)) { + lineWidthItem.value = value[lineWidthProperty]; + } else { + lineWidthItem.value = border.initialValues.get(lineWidthProperty); + } + lineWidthItem.priority = priority; + if (Object.hasOwn(value, lineStyleProperty)) { + lineStyleItem.value = value[lineStyleProperty]; + } else { + lineStyleItem.value = border.initialValues.get(lineStyleProperty); + } + lineStyleItem.priority = priority; + if (Object.hasOwn(value, lineColorProperty)) { + lineColorItem.value = value[lineColorProperty]; + } else { + lineColorItem.value = border.initialValues.get(lineColorProperty); + } + lineColorItem.priority = priority; + for (const position of positions) { + const positionProperty = `${prop1}-${position}`; const positionItem = getPropertyItem(positionProperty, properties); - if (nameItem.value) { - for (const positionValue of Object.values(value)) { - if (!matchesBorderShorthandValue(property, positionValue, nameItem.value, parseOpt)) { - nameItem.value = ""; - break; - } - } - } - positionItem.value = Object.values(value).join(" "); + positionItem.value = propertyValue; positionItem.priority = priority; for (const line of lines) { - const longhandProperty = `${prop1}-${prop2}-${line}`; + const longhandProperty = `${positionProperty}-${line}`; const longhandItem = getPropertyItem(longhandProperty, properties); - if (Object.hasOwn(value, longhandProperty)) { - const itemValue = value[longhandProperty]; - if (line === WIDTH) { - if (lineWidthItem.value) { - lineWidthItem.value = replacePositionValue( - itemValue, - splitValue(lineWidthItem.value), - prop2 - ); - } - } else if (line === STYLE) { - if (lineStyleItem.value) { - lineStyleItem.value = replacePositionValue( - itemValue, - splitValue(lineStyleItem.value), - prop2 - ); - } - } else if (line === COLOR) { - if (lineColorItem.value) { - lineColorItem.value = replacePositionValue( - itemValue, - splitValue(lineColorItem.value), - prop2 - ); - } - } - longhandItem.value = itemValue; - longhandItem.priority = priority; + const lineProperty = `${prop1}-${line}`; + if (Object.hasOwn(value, lineProperty)) { + longhandItem.value = value[lineProperty]; } else { - const itemValue = border.initialValues.get(`${prop1}-${line}`); - if (line === WIDTH) { - if (lineWidthItem.value) { - lineWidthItem.value = replacePositionValue( - itemValue, - splitValue(lineWidthItem.value), - prop2 - ); - } - } else if (line === STYLE) { - if (lineStyleItem.value) { - lineStyleItem.value = replacePositionValue( - itemValue, - splitValue(lineStyleItem.value), - prop2 - ); - } - } else if (line === COLOR) { - if (lineColorItem.value) { - lineColorItem.value = replacePositionValue( - itemValue, - splitValue(lineColorItem.value), - prop2 - ); - } - } - longhandItem.value = itemValue; - longhandItem.priority = priority; + longhandItem.value = border.initialValues.get(lineProperty); } + longhandItem.priority = priority; borderItems.set(longhandProperty, longhandItem); } - borderItems.set(nameProperty, nameItem); - borderItems.set(BORDER_IMAGE, imageItem); - borderItems.set(lineWidthProperty, lineWidthItem); - borderItems.set(lineStyleProperty, lineStyleItem); - borderItems.set(lineColorProperty, lineColorItem); borderItems.set(positionProperty, positionItem); - // Handle border shorthand + } + borderItems.set(property, nameItem); + borderItems.set(BORDER_IMAGE, imageItem); + borderItems.set(lineWidthProperty, lineWidthItem); + borderItems.set(lineStyleProperty, lineStyleItem); + borderItems.set(lineColorProperty, lineColorItem); + } +}; + +/** + * Prepares border properties by splitting shorthands and handling updates. + * + * @param {string} property - The border property name. + * @param {string|Array|Object} value - The value of the property. + * @param {string} priority - The priority of the property (e.g., "important"). + * @param {Map} properties - The map of all properties. + * @param {Object} [opt={}] - Parsing options. + * @returns {Map|undefined} A map of expanded or updated border properties. + */ +const prepareBorderProperties = (property, value, priority, properties, opt = {}) => { + if (typeof property !== "string" || value === null) { + return; + } + const { lines, name, positions } = borderElements; + if (!property.startsWith(name)) { + return; + } + let prop2, prop3; + if (property.length > name.length) { + // Check if next char is '-' + if (property.charCodeAt(name.length) !== 45) { + return; + } + // property is like "border-..." + const remainder = property.substring(name.length + 1); + const hyphenIndex = remainder.indexOf("-"); + if (hyphenIndex !== -1) { + prop2 = remainder.substring(0, hyphenIndex); + prop3 = remainder.substring(hyphenIndex + 1); } else { - const nameItem = getPropertyItem(prop1, properties); - const imageItem = getPropertyItem(BORDER_IMAGE, properties); - const lineWidthProperty = `${prop1}-width`; - const lineWidthItem = getPropertyItem(lineWidthProperty, properties); - const lineStyleProperty = `${prop1}-style`; - const lineStyleItem = getPropertyItem(lineStyleProperty, properties); - const lineColorProperty = `${prop1}-color`; - const lineColorItem = getPropertyItem(lineColorProperty, properties); - const propertyValue = Object.values(value).join(" "); - nameItem.value = propertyValue; - nameItem.priority = priority; - imageItem.value = propertyValue ? NONE : ""; - if (Object.hasOwn(value, lineWidthProperty)) { - lineWidthItem.value = value[lineWidthProperty]; - } else { - lineWidthItem.value = border.initialValues.get(lineWidthProperty); - } - lineWidthItem.priority = priority; - if (Object.hasOwn(value, lineStyleProperty)) { - lineStyleItem.value = value[lineStyleProperty]; - } else { - lineStyleItem.value = border.initialValues.get(lineStyleProperty); - } - lineStyleItem.priority = priority; - if (Object.hasOwn(value, lineColorProperty)) { - lineColorItem.value = value[lineColorProperty]; - } else { - lineColorItem.value = border.initialValues.get(lineColorProperty); - } - lineColorItem.priority = priority; - for (const position of positions) { - const positionProperty = `${prop1}-${position}`; - const positionItem = getPropertyItem(positionProperty, properties); - positionItem.value = propertyValue; - positionItem.priority = priority; - for (const line of lines) { - const longhandProperty = `${positionProperty}-${line}`; - const longhandItem = getPropertyItem(longhandProperty, properties); - const lineProperty = `${prop1}-${line}`; - if (Object.hasOwn(value, lineProperty)) { - longhandItem.value = value[lineProperty]; - } else { - longhandItem.value = border.initialValues.get(lineProperty); - } - longhandItem.priority = priority; - borderItems.set(longhandProperty, longhandItem); - } - borderItems.set(positionProperty, positionItem); - } - borderItems.set(property, nameItem); - borderItems.set(BORDER_IMAGE, imageItem); - borderItems.set(lineWidthProperty, lineWidthItem); - borderItems.set(lineStyleProperty, lineStyleItem); - borderItems.set(lineColorProperty, lineColorItem); + prop2 = remainder; } - } else { + } + // prop1 is assumed to be 'name' since we checked startsWith(name) + if ( + (positions.includes(prop2) && prop3 && !lines.includes(prop3)) || + (lines.includes(prop2) && prop3) + ) { return; } + const parts = { + prop1: name, + prop2, + prop3 + }; + const borderItems = new Map(); + if (typeof value === "string") { + prepareBorderStringValue({ + property, + value, + priority, + properties, + parts, + opt, + borderItems + }); + } else if (Array.isArray(value)) { + prepareBorderArrayValue({ + value, + priority, + properties, + parts, + opt, + borderItems + }); + } else if (value && typeof value === "object") { + prepareBorderObjectValue({ + property, + value, + priority, + properties, + parts, + opt, + borderItems + }); + } if (!borderItems.has(name)) { return; } const borderProps = new Map([[name, borderItems.get(name)]]); for (const line of lines) { const lineProperty = `${name}-${line}`; - const lineItem = borderItems.get(lineProperty) ?? - properties.get(lineProperty) ?? { - property: lineProperty, - value: "", - priority: "" - }; + const lineItem = + borderItems.get(lineProperty) ?? + properties.get(lineProperty) ?? + createPropertyItem(lineProperty); borderProps.set(lineProperty, lineItem); } for (const position of positions) { const positionProperty = `${name}-${position}`; - const positionItem = borderItems.get(positionProperty) ?? - properties.get(positionProperty) ?? { - property: positionProperty, - value: "", - priority: "" - }; + const positionItem = + borderItems.get(positionProperty) ?? + properties.get(positionProperty) ?? + createPropertyItem(positionProperty); borderProps.set(positionProperty, positionItem); for (const line of lines) { const longhandProperty = `${name}-${position}-${line}`; - const longhandItem = borderItems.get(longhandProperty) ?? - properties.get(longhandProperty) ?? { - property: longhandProperty, - value: "", - priority: "" - }; + const longhandItem = + borderItems.get(longhandProperty) ?? + properties.get(longhandProperty) ?? + createPropertyItem(longhandProperty); borderProps.set(longhandProperty, longhandItem); } } - const borderImageItem = borderItems.get(BORDER_IMAGE) ?? { - property: BORDER_IMAGE, - value: "", - priority: "" - }; + const borderImageItem = borderItems.get(BORDER_IMAGE) ?? createPropertyItem(BORDER_IMAGE); borderProps.set(BORDER_IMAGE, borderImageItem); return borderProps; }; +/** + * Generates a border line shorthand property if all line components are present. + * + * @param {Map} items - The map of collected property items. + * @param {string} property - The shorthand property name to generate. + * @param {string} prior - The priority of the property. + * @returns {Array} A key-value pair for the generated property. + */ const generateBorderLineShorthand = (items, property, prior) => { const values = []; for (const [, item] of items) { @@ -909,9 +1080,17 @@ const generateBorderLineShorthand = (items, property, prior) => { } const value = getPositionValue(values); const priority = prior ? prior : ""; - return [property, { property, value, priority }]; + return [property, createPropertyItem(property, value, priority)]; }; +/** + * Generates a border position shorthand property if all position components are present. + * + * @param {Map} items - The map of collected property items. + * @param {string} property - The shorthand property name to generate. + * @param {string} prior - The priority of the property. + * @returns {Array} A key-value pair for the generated property. + */ const generateBorderPositionShorthand = (items, property, prior) => { const values = []; for (const [, item] of items) { @@ -920,248 +1099,152 @@ const generateBorderPositionShorthand = (items, property, prior) => { } const value = values.join(" "); const priority = prior ? prior : ""; - return [property, { property, value, priority }]; + return [property, createPropertyItem(property, value, priority)]; }; +/** + * Generates a border name shorthand property if all components match. + * + * @param {Set|Array} items - The collection of property values. + * @param {string} property - The shorthand property name to generate. + * @param {string} prior - The priority of the property. + * @returns {Array|undefined} A key-value pair for the generated property or undefined. + */ const generateBorderNameShorthand = (items, property, prior) => { const values = new Set(items); if (values.size === 1) { const value = values.keys().next().value; const priority = prior ? prior : ""; - return [property, { property, value, priority }]; + return [property, createPropertyItem(property, value, priority)]; } }; -const prepareBorderShorthands = (properties) => { - const lineWidthItems = new Map(); - const lineWidthPriorItems = new Map(); - const lineStyleItems = new Map(); - const lineStylePriorItems = new Map(); - const lineColorItems = new Map(); - const lineColorPriorItems = new Map(); - const positionTopItems = new Map(); - const positionTopPriorItems = new Map(); - const positionRightItems = new Map(); - const positionRightPriorItems = new Map(); - const positionBottomItems = new Map(); - const positionBottomPriorItems = new Map(); - const positionLeftItems = new Map(); - const positionLeftPriorItems = new Map(); - for (const [property, { priority, value }] of properties) { - const [, positionPart, linePart] = property.split("-"); - switch (linePart) { - case WIDTH: { - if (priority) { - lineWidthPriorItems.set(property, { property, value, priority }); - } else { - lineWidthItems.set(property, { property, value, priority }); - } - break; - } - case STYLE: { - if (priority) { - lineStylePriorItems.set(property, { property, value, priority }); - } else { - lineStyleItems.set(property, { property, value, priority }); - } - break; - } - case COLOR: { - if (priority) { - lineColorPriorItems.set(property, { property, value, priority }); - } else { - lineColorItems.set(property, { property, value, priority }); - } - break; - } - default: - } - switch (positionPart) { - case TOP: { - if (priority) { - positionTopPriorItems.set(property, { property, value, priority }); - } else { - positionTopItems.set(property, { property, value, priority }); - } - break; - } - case RIGHT: { - if (priority) { - positionRightPriorItems.set(property, { property, value, priority }); - } else { - positionRightItems.set(property, { property, value, priority }); - } - break; - } - case BOTTOM: { - if (priority) { - positionBottomPriorItems.set(property, { property, value, priority }); - } else { - positionBottomItems.set(property, { property, value, priority }); - } - break; - } - case LEFT: { - if (priority) { - positionLeftPriorItems.set(property, { property, value, priority }); - } else { - positionLeftItems.set(property, { property, value, priority }); - } - break; - } - default: - } - } - if (lineWidthItems.size === 4) { - 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") ?? []; - if (property && item) { - properties.set(property, item); - } - } - if (lineStyleItems.size === 4) { - 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") ?? []; - if (property && item) { - properties.set(property, item); - } - } - if (lineColorItems.size === 4) { - 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") ?? []; - if (property && item) { - properties.set(property, item); - } +const borderCollections = { + [WIDTH]: { + shorthand: BORDER_WIDTH, + generator: generateBorderLineShorthand, + items: new Map(), + priorItems: new Map() + }, + [STYLE]: { + shorthand: BORDER_STYLE, + generator: generateBorderLineShorthand, + items: new Map(), + priorItems: new Map() + }, + [COLOR]: { + shorthand: BORDER_COLOR, + generator: generateBorderLineShorthand, + items: new Map(), + priorItems: new Map() + }, + [TOP]: { + shorthand: BORDER_TOP, + generator: generateBorderPositionShorthand, + items: new Map(), + priorItems: new Map() + }, + [RIGHT]: { + shorthand: BORDER_RIGHT, + generator: generateBorderPositionShorthand, + items: new Map(), + priorItems: new Map() + }, + [BOTTOM]: { + shorthand: BORDER_BOTTOM, + generator: generateBorderPositionShorthand, + items: new Map(), + priorItems: new Map() + }, + [LEFT]: { + shorthand: BORDER_LEFT, + generator: generateBorderPositionShorthand, + items: new Map(), + priorItems: new Map() } - const nameItems = []; - const namePriorItems = []; - if (positionTopItems.size === 3) { - const [property, item] = generateBorderPositionShorthand(positionTopItems, BORDER_TOP) ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); - } - } - } - } else if (positionTopPriorItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionTopPriorItems, BORDER_TOP, "important") ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - namePriorItems.push(itemValue); - } - } - } +}; + +/** + * Processes and consolidates border-related longhands into shorthands where possible. + * + * @param {Map} properties - The map of current properties. + * @returns {Map} The updated map with consolidated border properties. + */ +const prepareBorderShorthands = (properties) => { + for (const collection of Object.values(borderCollections)) { + collection.items.clear(); + collection.priorItems.clear(); } - if (positionRightItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionRightItems, BORDER_RIGHT) ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); - } - } - } - } else if (positionRightPriorItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionRightPriorItems, BORDER_RIGHT, "important") ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); - } + for (const [property, item] of properties) { + const { priority, value } = item; + let positionPart, linePart; + // We can assume property starts with "border-" + const firstHyphen = property.indexOf("-"); + if (firstHyphen !== -1) { + const remainder = property.substring(firstHyphen + 1); + const secondHyphen = remainder.indexOf("-"); + if (secondHyphen !== -1) { + positionPart = remainder.substring(0, secondHyphen); + linePart = remainder.substring(secondHyphen + 1); + } else { + positionPart = remainder; + linePart = undefined; } } - } - if (positionBottomItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionBottomItems, BORDER_BOTTOM) ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); - } + if (linePart && borderCollections[linePart]) { + const collection = borderCollections[linePart]; + if (priority) { + collection.priorItems.set(property, { property, value, priority }); + } else { + collection.items.set(property, { property, value, priority }); } } - } else if (positionBottomPriorItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionBottomPriorItems, BORDER_BOTTOM, "important") ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); - } + if (positionPart && borderCollections[positionPart]) { + const collection = borderCollections[positionPart]; + if (priority) { + collection.priorItems.set(property, { property, value, priority }); + } else { + collection.items.set(property, { property, value, priority }); } } } - if (positionLeftItems.size === 3) { - const [property, item] = generateBorderPositionShorthand(positionLeftItems, BORDER_LEFT) ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); + const nameItems = []; + const namePriorItems = []; + for (const [key, collection] of Object.entries(borderCollections)) { + const { shorthand, generator, items, priorItems } = collection; + const requiredSize = [WIDTH, STYLE, COLOR].includes(key) ? 4 : 3; + if (items.size === requiredSize) { + const [property, item] = generator(items, shorthand) ?? []; + if (property && item) { + properties.set(property, item); + // For position shorthands (top/right/bottom/left), check border-image compatibility + if ([TOP, RIGHT, BOTTOM, LEFT].includes(key)) { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { + nameItems.push(item.value); + } + } } } - } - } else if (positionLeftPriorItems.size === 3) { - const [property, item] = - generateBorderPositionShorthand(positionLeftPriorItems, BORDER_LEFT, "important") ?? []; - if (property && item) { - properties.set(property, item); - if (properties.has(BORDER_IMAGE)) { - const { value: imageValue } = properties.get(BORDER_IMAGE); - if (imageValue === NONE) { - const { value: itemValue } = item; - nameItems.push(itemValue); + } else if (priorItems.size === requiredSize) { + const [property, item] = generator(priorItems, shorthand, "important") ?? []; + if (property && item) { + properties.set(property, item); + // For position shorthands + if ([TOP, RIGHT, BOTTOM, LEFT].includes(key)) { + if (properties.has(BORDER_IMAGE)) { + const { value: imageValue } = properties.get(BORDER_IMAGE); + if (imageValue === NONE) { + namePriorItems.push(item.value); + } + } } } } } const mixedPriorities = nameItems.length && namePriorItems.length; - const imageItem = { - property: BORDER_IMAGE, - value: NONE, - priority: "" - }; + const imageItem = createPropertyItem(BORDER_IMAGE, NONE); if (nameItems.length === 4) { const [property, item] = generateBorderNameShorthand(nameItems, BORDER) ?? []; if (property && item) { @@ -1212,39 +1295,182 @@ const prepareBorderShorthands = (properties) => { return properties; }; +/** + * Processes shorthand properties from the shorthands map. + * + * @param {Map} shorthands - The map containing shorthand property groups. + * @returns {Map} A map of processed shorthand properties. + */ +const processShorthandProperties = (shorthands) => { + const shorthandItems = new Map(); + for (const [property, item] of shorthands) { + const shorthandItem = shorthandProperties.get(property); + if (item.size === shorthandItem.shorthandFor.size && shorthandItem.position) { + const positionValues = []; + let priority = ""; + for (const { value: longhandValue, priority: longhandPriority } of item.values()) { + positionValues.push(longhandValue); + if (longhandPriority) { + priority = longhandPriority; + } + } + const value = getPositionValue(positionValues, shorthandItem.position); + shorthandItems.set(property, createPropertyItem(property, value, priority)); + } + } + return shorthandItems; +}; + +/** + * Processes border properties from the borders map, expanding and normalizing them. + * + * @param {Map} borders - The map containing accumulated border properties. + * @param {Object} parseOpt - Options for parsing values. + * @returns {Map} A map of fully processed and normalized border properties. + */ +const processBorderProperties = (borders, parseOpt) => { + const { positions } = borderElements; + const longhandProperties = new Map(); + for (const [property, item] of borders) { + if (shorthandProperties.has(property)) { + const { value, priority } = item; + if (property === BORDER) { + const lineItems = border.parse(value, parseOpt); + for (const [key, initialValue] of border.initialValues) { + if (!Object.hasOwn(lineItems, key)) { + lineItems[key] = initialValue; + } + } + for (const lineProperty of Object.keys(lineItems)) { + let namePart, linePart; + const hyphenIndex = lineProperty.indexOf("-"); + if (hyphenIndex !== -1) { + namePart = lineProperty.substring(0, hyphenIndex); + linePart = lineProperty.substring(hyphenIndex + 1); + } else { + // fallback for safety, though lineProperty from border.parse keys + // should have hyphen + namePart = lineProperty; + linePart = ""; + } + const lineValue = lineItems[lineProperty]; + for (const position of positions) { + const longhandProperty = `${namePart}-${position}-${linePart}`; + const longhandItem = createPropertyItem(longhandProperty, lineValue, priority); + if (longhandProperties.has(longhandProperty)) { + const { priority: longhandPriority } = longhandProperties.get(longhandProperty); + if (!longhandPriority) { + longhandProperties.delete(longhandProperty); + longhandProperties.set(longhandProperty, longhandItem); + } + } else { + longhandProperties.set(longhandProperty, longhandItem); + } + } + } + if (value) { + longhandProperties.set(BORDER_IMAGE, createPropertyItem(BORDER_IMAGE, NONE, priority)); + } + } else { + const shorthandItem = shorthandProperties.get(property); + const parsedItem = shorthandItem.parse(value, parseOpt); + if (Array.isArray(parsedItem)) { + let namePart, linePart; + const hyphenIndex = property.indexOf("-"); + if (hyphenIndex !== -1) { + namePart = property.substring(0, hyphenIndex); + linePart = property.substring(hyphenIndex + 1); + } else { + namePart = property; + } + for (const position of positions) { + const longhandProperty = `${namePart}-${position}-${linePart}`; + const longhandValue = getPositionValue(parsedItem, position); + const longhandItem = createPropertyItem(longhandProperty, longhandValue, priority); + if (longhandProperties.has(longhandProperty)) { + const { priority: longhandPriority } = longhandProperties.get(longhandProperty); + if (!longhandPriority) { + longhandProperties.delete(longhandProperty); + longhandProperties.set(longhandProperty, longhandItem); + } + } else { + longhandProperties.set(longhandProperty, longhandItem); + } + } + } else if (parsedItem) { + for (const [key, initialValue] of shorthandItem.initialValues) { + if (!Object.hasOwn(parsedItem, key)) { + parsedItem[key] = initialValue; + } + } + for (const longhandProperty of Object.keys(parsedItem)) { + const longhandValue = parsedItem[longhandProperty]; + const longhandItem = createPropertyItem(longhandProperty, longhandValue, priority); + if (longhandProperties.has(longhandProperty)) { + const { priority: longhandPriority } = longhandProperties.get(longhandProperty); + if (!longhandPriority) { + longhandProperties.delete(longhandProperty); + longhandProperties.set(longhandProperty, longhandItem); + } + } else { + longhandProperties.set(longhandProperty, longhandItem); + } + } + } + } + } else if (longhandProperties.has(property)) { + const { priority } = longhandProperties.get(property); + if (!priority) { + longhandProperties.delete(property); + longhandProperties.set(property, item); + } + } else { + longhandProperties.set(property, item); + } + } + const borderItems = prepareBorderShorthands(longhandProperties); + return borderItems; +}; + +/** + * Normalize and prepare CSS properties, handling shorthands and longhands. + * + * @param {Map} properties - The initial map of properties. + * @param {Object} [opt={}] - Parsing options. + * @returns {Map} The normalized map of properties. + */ const prepareProperties = (properties, opt = {}) => { const { globalObject, options } = opt; const parseOpt = { globalObject, options }; - const { positions } = borderElements; const parsedProperties = new Map(); - const prepareShorthands = new Map(); - const borderProps = new Map(); + const shorthands = new Map(); + const borders = new Map(); let hasPrecedingBackground = false; for (const [property, item] of properties) { const { value, priority } = item; const { logicalPropertyGroup: shorthandProperty } = propertyDefinitions.get(property) ?? {}; if (borderProperties.has(property)) { - borderProps.set(property, { property, value, priority }); + borders.set(property, { property, value, priority }); } else if (shorthandProperties.has(shorthandProperty)) { - if (!prepareShorthands.has(shorthandProperty)) { - prepareShorthands.set(shorthandProperty, new Map()); + if (!shorthands.has(shorthandProperty)) { + shorthands.set(shorthandProperty, new Map()); } - const longhandItems = prepareShorthands.get(shorthandProperty); + const longhandItems = shorthands.get(shorthandProperty); if (longhandItems.size) { const firstPropertyKey = longhandItems.keys().next().value; const { priority: firstPropertyPriority } = longhandItems.get(firstPropertyKey); if (priority === firstPropertyPriority) { longhandItems.set(property, { property, value, priority }); - prepareShorthands.set(shorthandProperty, longhandItems); + shorthands.set(shorthandProperty, longhandItems); } else { parsedProperties.delete(shorthandProperty); } } else { longhandItems.set(property, { property, value, priority }); - prepareShorthands.set(shorthandProperty, longhandItems); + shorthands.set(shorthandProperty, longhandItems); } parsedProperties.set(property, item); } else if (shorthandProperties.has(property)) { @@ -1264,42 +1490,37 @@ const prepareProperties = (properties, opt = {}) => { } const { position } = longhandItem; const longhandValue = getPositionValue([parsedValue], position); - parsedProperties.set(longhandProperty, { - property: longhandProperty, - value: longhandValue, - priority - }); + parsedProperties.set( + longhandProperty, + createPropertyItem(longhandProperty, longhandValue, priority) + ); } } else if (parsedValue) { for (const longhandProperty of Object.keys(parsedValue)) { const longhandValue = parsedValue[longhandProperty]; - parsedProperties.set(longhandProperty, { - property: longhandProperty, - value: longhandValue, - priority - }); + parsedProperties.set( + longhandProperty, + createPropertyItem(longhandProperty, longhandValue, priority) + ); } } } else if (parsedValues && typeof parsedValues !== "string") { for (const longhandProperty of Object.keys(parsedValues)) { const longhandValue = parsedValues[longhandProperty]; - parsedProperties.set(longhandProperty, { - property: longhandProperty, - value: longhandValue, - priority - }); + parsedProperties.set( + longhandProperty, + createPropertyItem(longhandProperty, longhandValue, priority) + ); } } if (!omitShorthandProperty) { if (property === BACKGROUND) { hasPrecedingBackground = true; - parsedProperties.set(property, { property, value, priority }); - } else { - parsedProperties.set(property, { property, value, priority }); } + parsedProperties.set(property, createPropertyItem(property, value, priority)); } } else { - parsedProperties.set(property, { property, value, priority }); + parsedProperties.set(property, createPropertyItem(property, value, priority)); if (hasPrecedingBackground) { const { value: shorthandValue, priority: shorthandPriority } = properties.get(BACKGROUND); if ((!shorthandPriority || priority) && !hasVarFunc(shorthandValue)) { @@ -1309,142 +1530,35 @@ const prepareProperties = (properties, opt = {}) => { parseOpt ); properties.delete(BACKGROUND); - properties.set(BACKGROUND, { - property: BACKGROUND, - value: replacedShorthandValue, - priority: shorthandPriority - }); + properties.set( + BACKGROUND, + createPropertyItem(BACKGROUND, replacedShorthandValue, shorthandPriority) + ); } } } } - if (prepareShorthands.size) { - for (const [property, item] of prepareShorthands) { - const shorthandItem = shorthandProperties.get(property); - if (item.size === shorthandItem.shorthandFor.size && shorthandItem.position) { - const positionValues = []; - let priority = ""; - for (const { value: longhandValue, priority: longhandPriority } of item.values()) { - positionValues.push(longhandValue); - if (longhandPriority) { - priority = longhandPriority; - } - } - const value = getPositionValue(positionValues, shorthandItem.position); - parsedProperties.set(property, { - property, - value, - priority - }); - } + if (shorthands.size) { + const shorthandItems = processShorthandProperties(shorthands); + for (const [property, item] of shorthandItems) { + parsedProperties.set(property, item); } } - if (borderProps.size) { - const longhandProperties = new Map(); - for (const [property, item] of borderProps) { - if (shorthandProperties.has(property)) { - const { value, priority } = item; - if (property === BORDER) { - const lineItems = border.parse(value, parseOpt); - for (const [key, initialValue] of border.initialValues) { - if (!Object.hasOwn(lineItems, key)) { - lineItems[key] = initialValue; - } - } - for (const lineProperty of Object.keys(lineItems)) { - const [namePart, linePart] = lineProperty.split("-"); - const lineValue = lineItems[lineProperty]; - for (const position of positions) { - const longhandProperty = `${namePart}-${position}-${linePart}`; - const longhandItem = { - property: longhandProperty, - value: lineValue, - priority - }; - if (longhandProperties.has(longhandProperty)) { - const { priority: longhandPriority } = longhandProperties.get(longhandProperty); - if (!longhandPriority) { - longhandProperties.delete(longhandProperty); - longhandProperties.set(longhandProperty, longhandItem); - } - } else { - longhandProperties.set(longhandProperty, longhandItem); - } - } - } - if (value) { - longhandProperties.set(BORDER_IMAGE, { - property: BORDER_IMAGE, - value: NONE, - priority - }); - } - } else { - 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 = getPositionValue(parsedItem, position); - const longhandItem = { - property: longhandProperty, - value: longhandValue, - priority - }; - if (longhandProperties.has(longhandProperty)) { - const { priority: longhandPriority } = longhandProperties.get(longhandProperty); - if (!longhandPriority) { - longhandProperties.delete(longhandProperty); - longhandProperties.set(longhandProperty, longhandItem); - } - } else { - longhandProperties.set(longhandProperty, longhandItem); - } - } - } else if (parsedItem) { - for (const [key, initialValue] of shorthandItem.initialValues) { - if (!Object.hasOwn(parsedItem, key)) { - parsedItem[key] = initialValue; - } - } - for (const longhandProperty of Object.keys(parsedItem)) { - const longhandValue = parsedItem[longhandProperty]; - const longhandItem = { - property: longhandProperty, - value: longhandValue, - priority - }; - if (longhandProperties.has(longhandProperty)) { - const { priority: longhandPriority } = longhandProperties.get(longhandProperty); - if (!longhandPriority) { - longhandProperties.delete(longhandProperty); - longhandProperties.set(longhandProperty, longhandItem); - } - } else { - longhandProperties.set(longhandProperty, longhandItem); - } - } - } - } - } else if (longhandProperties.has(property)) { - const { priority } = longhandProperties.get(property); - if (!priority) { - longhandProperties.delete(property); - longhandProperties.set(property, item); - } - } else { - longhandProperties.set(property, item); - } - } - const normalizedProperties = prepareBorderShorthands(longhandProperties); - for (const [property, item] of normalizedProperties) { + if (borders.size) { + const borderItems = processBorderProperties(borders, parseOpt); + for (const [property, item] of borderItems) { parsedProperties.set(property, item); } } return parsedProperties; }; +/** + * Cleans up redundancy in border properties by removing longhands that are covered by shorthands. + * + * @param {Map} properties - The map of properties to normalize. + * @returns {Map} The normalized properties map. + */ const normalizeProperties = (properties) => { const { lines, name, positions } = borderElements; if (properties.has(name)) { From f9989866ae5031900bf225c49cebcbce002a0a75 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 19:46:01 +0900 Subject: [PATCH 14/29] Reduce onchange callback if property is a shorthand --- lib/CSSStyleDeclaration.js | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 23eff090..84dbd926 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -358,7 +358,12 @@ class CSSStyleDeclaration { */ _borderSetter(prop, val, prior) { const properties = new Map(); + let originalText = ""; if (prop === "border") { + this._updating = true; + if (typeof this._onChange === "function") { + originalText = this.cssText; + } let priority = ""; if (typeof prior === "string") { priority = prior; @@ -388,6 +393,12 @@ class CSSStyleDeclaration { const { priority, value } = item; this._setProperty(property, value, priority); } + if (prop === "border") { + if (typeof this._onChange === "function" && this.cssText !== originalText) { + this._onChange(this.cssText); + } + this._updating = false; + } } /** @@ -400,6 +411,13 @@ class CSSStyleDeclaration { if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { return; } + let originalText = ""; + if (prop === "flex") { + this._updating = true; + if (typeof this._onChange === "function") { + originalText = this.cssText; + } + } const shorthandPriority = this._priorities.get(shorthandProperty); this.removeProperty(shorthandProperty); let priority = ""; @@ -451,6 +469,12 @@ class CSSStyleDeclaration { } } } + if (prop === "flex") { + if (typeof this._onChange === "function" && this.cssText !== originalText) { + this._onChange(this.cssText); + } + this._updating = false; + } } /** @@ -462,6 +486,13 @@ class CSSStyleDeclaration { if (!shorthandProperties.has(prop)) { return; } + let originalText = ""; + if (prop === "margin" || prop === "padding") { + this._updating = true; + if (typeof this._onChange === "function") { + originalText = this.cssText; + } + } const shorthandValues = []; if (Array.isArray(val)) { shorthandValues.push(...val); @@ -498,6 +529,12 @@ class CSSStyleDeclaration { const shorthandValue = getPositionValue(shorthandValues, position); this._setProperty(prop, shorthandValue, priority); } + if (prop === "margin" || prop === "padding") { + if (typeof this._onChange === "function" && this.cssText !== originalText) { + this._onChange(this.cssText); + } + this._updating = false; + } } /** From d7716cf3e1856616256603245ddf5231ab1f148d Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 21:53:13 +0900 Subject: [PATCH 15/29] Update CSSStyleDeclaration.js --- lib/CSSStyleDeclaration.js | 617 +++++++++++++++++-------------------- lib/handlers/border.js | 52 ++++ lib/handlers/flex.js | 79 +++++ lib/handlers/index.js | 12 + lib/handlers/position.js | 107 +++++++ lib/utils/constants.js | 4 + 6 files changed, 537 insertions(+), 334 deletions(-) create mode 100644 lib/handlers/border.js create mode 100644 lib/handlers/flex.js create mode 100644 lib/handlers/index.js create mode 100644 lib/handlers/position.js create mode 100644 lib/utils/constants.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 84dbd926..4642fb73 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -7,35 +7,47 @@ const generatedProperties = require("./generated/properties"); const propertyDefinitions = require("./generated/propertyDefinitions"); const { - borderProperties, - getPositionValue, + handleBorder, + handleFlex, + handlePositionShorthand, + handlePositionLonghand +} = require("./handlers"); +const { normalizeProperties, - prepareBorderProperties, prepareProperties, shorthandProperties } = require("./normalize"); const { hasVarFunc, - isGlobalKeyword, parseCSS, parsePropertyValue, prepareValue } = require("./parsers"); +const { ELEMENT_NODE, NO_MODIFICATION_ALLOWED_ERR } = require("./utils/constants"); const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); -const ELEMENT_NODE = 1; +// Internal Setters (Dynamic Assignment) +// These handlers are mapped to internal method names expected by generated properties. +const internalSetters = { + _borderSetter: handleBorder, + _flexBoxSetter: handleFlex, + _positionShorthandSetter: handlePositionShorthand, + _positionLonghandSetter: handlePositionLonghand +}; /** * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface */ 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 + * Creates a new CSSStyleDeclaration instance. + * + * @param {object} globalObject - Window object + * @param {object} [opt] - Options + * @param {object} [opt.context] - Element or CSSStyleRule + * @param {"specifiedValue"|"computedValue"} [opt.format="specifiedValue"] - "specifiedValue" or "computedValue" + * @param {Function} [opt.onChange] - Callback when cssText is changed or the property is removed */ constructor(globalObject, opt = {}) { const { context, format, onChange } = opt; @@ -71,6 +83,11 @@ class CSSStyleDeclaration { }; } + /** + * Textual representation of the declaration block. + * + * @type {string} + */ get cssText() { if (this._computed) { return ""; @@ -102,110 +119,50 @@ class CSSStyleDeclaration { return parts.join(" "); } + /** + * Sets the textual representation of the declaration block. + * This will parse the given text and update the properties accordingly. + * + * @param {string} val + */ set cssText(val) { - if (this._readonly) { - const msg = "cssText can not be modified."; - const name = "NoModificationAllowedError"; - throw new this._global.DOMException(msg, name); - } + this._checkReadonly(); if (this._updating) { return; } - Array.prototype.splice.call(this, 0, this._length); - this._values.clear(); - this._priorities.clear(); + + this._clearProperties(); this._updating = true; + try { - // TBD: use cache? - const valueObj = parseCSS( - val, - { - globalObject: this._global, - options: { - context: "declarationList", - parseValue: false - } - }, - true - ); + const valueObj = this._parseCSSText(val); 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; - } - if (item.type === "Declaration") { - const { - important, - property, - value: { value } - } = item; - const priority = important ? "important" : ""; - const isCustomProperty = property.startsWith("--"); - if (isCustomProperty || hasVarFunc(value)) { - if (properties.has(property)) { - const { priority: itemPriority } = properties.get(property); - if (!itemPriority) { - properties.set(property, { property, value, priority }); - } - } else { - properties.set(property, { property, value, priority }); - } - } else { - // TBD: use cache? - const parsedValue = parsePropertyValue(property, value, { - globalObject: this._global, - options: this._options - }); - if (parsedValue) { - if (properties.has(property)) { - const { priority: itemPriority } = properties.get(property); - if (!itemPriority) { - properties.set(property, { property, value, priority }); - } - } else { - properties.set(property, { property, value, priority }); - } - } else { - this.removeProperty(property); - } - } - } - } - const parsedProperties = prepareProperties(properties, { - globalObject: this._global, - options: this._options - }); - for (const [property, item] of parsedProperties) { - const { priority, value } = item; - this._priorities.set(property, priority); - this.setProperty(property, value, priority); - } + const properties = this._extractDeclarations(valueObj.children); + this._applyProperties(properties); } } catch { return; } + this._updating = false; - if (typeof this._onChange === "function") { - this._onChange(this.cssText); - } + this._notifyChange(); } + /** + * The number of properties in the declaration block. + * + * @type {number} + */ get length() { return this._length; } - // 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. + /** + * 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. + * + * @param {number} len + */ set length(len) { for (let i = len; i < this._length; i++) { delete this[i]; @@ -214,7 +171,10 @@ class CSSStyleDeclaration { } /** - * @param {...number} args + * Returns the property name at the given index. + * + * @param {number} index + * @returns {string} */ item(...args) { if (!args.length) { @@ -230,7 +190,10 @@ class CSSStyleDeclaration { } /** + * Returns the value of the property. + * * @param {string} property + * @returns {string} */ getPropertyValue(property) { if (this._values.has(property)) { @@ -240,23 +203,24 @@ class CSSStyleDeclaration { } /** + * Returns the priority of the property (e.g. "important"). + * * @param {string} property + * @returns {string} */ getPropertyPriority(property) { return this._priorities.get(property) || ""; } /** - * @param {string} prop - * @param {string} val - * @param {string} prior + * Sets a property with a value and optional priority. + * + * @param {string} prop - Property name + * @param {string} val - Property value + * @param {string} [prior] - Priority (e.g. "important") */ setProperty(prop, val, prior) { - if (this._readonly) { - const msg = `Property ${prop} can not be modified.`; - const name = "NoModificationAllowedError"; - throw new this._global.DOMException(msg, name); - } + this._checkReadonly(prop); const value = prepareValue(val, this._global); if (value === "") { this[prop] = ""; @@ -282,36 +246,87 @@ class CSSStyleDeclaration { } /** + * Removes a property. + * * @param {string} property + * @returns {string} The value of the removed property */ removeProperty(property) { - if (this._readonly) { - const msg = `Property ${property} can not be modified.`; - const name = "NoModificationAllowedError"; - throw new this._global.DOMException(msg, name); - } + this._checkReadonly(property); 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); - } + + const removed = this._removeIndex(property); + if (removed) { + this._notifyChange(); } return prevValue; } + /** + * The CSS rule that contains this declaration block. + * + * @returns {object|null} + */ get parentRule() { return this._parentRule; } + /** + * Iterator for the declaration block. + * Allows `for (const prop of style)` and `[...style]`. + */ + *[Symbol.iterator]() { + for (let i = 0; i < this._length; i++) { + yield this[i]; + } + } + // Internal methods /** + * Checks if the declaration is readonly. + * + * @private + * @param {string} [property] - The property name to check, used for the error message. + * @throws {DOMException} If the declaration is readonly. + */ + _checkReadonly(property) { + if (this._readonly) { + const msg = property + ? `Property ${property} can not be modified.` + : "cssText can not be modified."; + throw new this._global.DOMException(msg, NO_MODIFICATION_ALLOWED_ERR); + } + } + + /** + * Triggers the onChange callback if it's a function and the text has changed. + * + * @private + * @param {string|null} [originalText] - The CSS text before the change. If provided, checking compares this with new cssText. + */ + _notifyChange(originalText = null) { + if (typeof this._onChange !== "function") { + return; + } + if (this._updating) { + return; + } + const newText = this.cssText; + if (originalText !== null && originalText === newText) { + return; + } + this._onChange(newText); + } + + /** + * Sets a property internally. + * + * @private * @param {string} property * @param {string} val * @param {string} priority @@ -324,267 +339,192 @@ class CSSStyleDeclaration { this.removeProperty(property); return; } - let originalText = ""; - if (typeof this._onChange === "function") { - originalText = this.cssText; - } - if (this._values.has(property)) { - const index = Array.prototype.indexOf.call(this, property); - // The property already exists but is not indexed into `this` so add it. - if (index < 0) { - this[this._length] = property; - this._length++; - } - } else { - // New property. - this[this._length] = property; - this._length++; + // Performance Optimization: + // Only calculate cssText if we are notifying a change AND not currently in a bulk update. + const originalText = (typeof this._onChange === "function" && !this._updating) ? this.cssText : null; + + // Performance Optimization: + // Only scan array to add index if the property is not already known. + if (!this._values.has(property)) { + this._addIndex(property); } + 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); - } + this._notifyChange(originalText); } /** - * @param {string} prop - * @param {object|Array|string} val - * @param {string} prior + * Clears all properties. + * + * @private */ - _borderSetter(prop, val, prior) { - const properties = new Map(); - let originalText = ""; - if (prop === "border") { - this._updating = true; - if (typeof this._onChange === "function") { - originalText = this.cssText; - } - 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); + _clearProperties() { + Array.prototype.splice.call(this, 0, this._length); + this._values.clear(); + this._priorities.clear(); + } + + /** + * Adds a property to the indexed properties list if not already present. + * + * @private + * @param {string} property + */ + _addIndex(property) { + const index = Array.prototype.indexOf.call(this, property); + if (index < 0) { + this[this._length] = property; + this._length++; } - if (prop === "border") { - if (typeof this._onChange === "function" && this.cssText !== originalText) { - this._onChange(this.cssText); - } - this._updating = false; + } + + /** + * Removes a property from the indexed properties list. + * + * @private + * @param {string} property + * @returns {boolean} True if the property was removed. + */ + _removeIndex(property) { + const index = Array.prototype.indexOf.call(this, property); + if (index >= 0) { + Array.prototype.splice.call(this, index, 1); + return true; } + return false; } /** - * @param {string} prop + * Parses CSS text. + * + * @private * @param {string} val - * @param {string} prior - * @param {string} shorthandProperty + * @returns {object} The parsed CSS object. */ - _flexBoxSetter(prop, val, prior, shorthandProperty) { - if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { - return; - } - let originalText = ""; - if (prop === "flex") { - this._updating = true; - if (typeof this._onChange === "function") { - originalText = this.cssText; - } - } - const shorthandPriority = this._priorities.get(shorthandProperty); - this.removeProperty(shorthandProperty); - let priority = ""; - if (typeof prior === "string") { - priority = prior; - } else { - priority = this._priorities.get(prop) ?? ""; - } - this.removeProperty(prop); - if (shorthandPriority && priority) { - this._setProperty(prop, val); - } else { - this._setProperty(prop, val, priority); - } - if (val && !hasVarFunc(val)) { - const longhandValues = []; - const shorthandItem = shorthandProperties.get(shorthandProperty); - let hasGlobalKeyword = false; - for (const [longhandProperty] of shorthandItem.shorthandFor) { - if (longhandProperty === prop) { - if (isGlobalKeyword(val)) { - hasGlobalKeyword = true; - } - longhandValues.push(val); - } else { - const longhandValue = this.getPropertyValue(longhandProperty); - const longhandPriority = this._priorities.get(longhandProperty) ?? ""; - if (!longhandValue || longhandPriority !== priority) { - break; - } - if (isGlobalKeyword(longhandValue)) { - hasGlobalKeyword = true; - } - longhandValues.push(longhandValue); + _parseCSSText(val) { + // TBD: use cache? + return parseCSS( + val, + { + globalObject: this._global, + options: { + context: "declarationList", + parseValue: false } + }, + true + ); + } + + /** + * Extracts declarations from the parsed CSS children. + * + * @private + * @param {Array} children + * @returns {Map} A map of properties to their values and priorities. + */ + _extractDeclarations(children) { + const properties = new Map(); + let shouldSkipNext = false; + for (const item of children) { + if (item.type === "Atrule") { + continue; } - 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); - } - } + if (item.type === "Rule") { + shouldSkipNext = true; + continue; } - } - if (prop === "flex") { - if (typeof this._onChange === "function" && this.cssText !== originalText) { - this._onChange(this.cssText); + if (shouldSkipNext === true) { + shouldSkipNext = false; + continue; + } + if (item.type === "Declaration") { + this._processDeclaration(item, properties); } - this._updating = false; } + return properties; } /** - * @param {string} prop - * @param {Array|string} val - * @param {string} prior + * Processes a single declaration and adds it to the properties map. + * + * @private + * @param {object} item - The declaration item. + * @param {Map} properties - The map to add the property to. */ - _positionShorthandSetter(prop, val, prior) { - if (!shorthandProperties.has(prop)) { - return; - } - let originalText = ""; - if (prop === "margin" || prop === "padding") { - this._updating = true; - if (typeof this._onChange === "function") { - originalText = this.cssText; - } - } - 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; + _processDeclaration(item, properties) { + const { + important, + property, + value: { value } + } = item; + const priority = important ? "important" : ""; + const isCustomProperty = property.startsWith("--"); + + if (isCustomProperty || hasVarFunc(value)) { + this._addPropertyToMap(properties, property, value, priority); } 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); + // TBD: use cache? + const parsedValue = parsePropertyValue(property, value, { + globalObject: this._global, + options: this._options + }); + if (parsedValue) { + this._addPropertyToMap(properties, property, value, priority); } else { - const longhandPriority = this._priorities.get(longhandProperty) ?? ""; - if (longhandPriority) { - hasPriority = true; - } else { - this._setProperty(longhandProperty, longhandValue, priority); - } - } - } - if (hasPriority) { - this.removeProperty(prop); - } else { - const shorthandValue = getPositionValue(shorthandValues, position); - this._setProperty(prop, shorthandValue, priority); - } - if (prop === "margin" || prop === "padding") { - if (typeof this._onChange === "function" && this.cssText !== originalText) { - this._onChange(this.cssText); + this.removeProperty(property); } - this._updating = false; } } /** - * @param {string} prop - * @param {string} val - * @param {string} prior - * @param {string} shorthandProperty + * Adds a property to the properties map, handling existing priority. + * + * @private + * @param {Map} properties + * @param {string} property + * @param {string} value + * @param {string} priority */ - _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; + _addPropertyToMap(properties, property, value, priority) { + if (properties.has(property)) { + const { priority: itemPriority } = properties.get(property); + if (!itemPriority) { + properties.set(property, { property, value, priority }); + } } else { - priority = this._priorities.get(prop) ?? ""; + properties.set(property, { property, value, priority }); } - 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); - } + } + + /** + * Applies the properties to the declaration block. + * + * @private + * @param {Map} properties + */ + _applyProperties(properties) { + const parsedProperties = prepareProperties(properties, { + globalObject: this._global, + options: this._options + }); + for (const [property, item] of parsedProperties) { + const { priority, value } = item; + this._priorities.set(property, priority); + this.setProperty(property, value, priority); } } } -// Properties +// Define standard CSS properties on the prototype from the generated properties map. Object.defineProperties(CSSStyleDeclaration.prototype, generatedProperties); -// Additional properties +// Define alias and legacy properties based on property definitions. for (const definition of propertyDefinitions.values()) { const { legacyAliasOf, name, styleDeclaration } = definition; const property = legacyAliasOf ?? name; @@ -596,6 +536,15 @@ for (const definition of propertyDefinitions.values()) { } } +// Assign the internal setters to the prototype. +// These methods are used by the properties generated in generated/properties.js. +for (const [methodName, handler] of Object.entries(internalSetters)) { + // NOTE: Arrow functions cannot be used here because 'this' must be bound to the CSSStyleDeclaration instance. + CSSStyleDeclaration.prototype[methodName] = function(...args) { + handler(this, ...args); + }; +} + module.exports = { CSSStyleDeclaration }; diff --git a/lib/handlers/border.js b/lib/handlers/border.js new file mode 100644 index 00000000..08b832c5 --- /dev/null +++ b/lib/handlers/border.js @@ -0,0 +1,52 @@ +"use strict"; + +const { borderProperties, prepareBorderProperties } = require("../normalize"); + +/** + * @param {object} styleDecl - CSSStyleDeclaration instance + * @param {string} prop + * @param {object|Array|string} val + * @param {string} prior + */ +exports.handleBorder = (styleDecl, prop, val, prior) => { + const properties = new Map(); + let originalText = null; + if (prop === "border") { + styleDecl._updating = true; + if (typeof styleDecl._onChange === "function") { + originalText = styleDecl.cssText; + } + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = styleDecl._priorities.get(prop) ?? ""; + } + properties.set(prop, { propery: prop, value: val, priority }); + } else { + for (let i = 0; i < styleDecl._length; i++) { + const property = styleDecl[i]; + if (borderProperties.has(property)) { + const value = styleDecl.getPropertyValue(property); + const longhandPriority = styleDecl._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: styleDecl._global, + options: styleDecl._options + }); + for (const [property, item] of parsedProperties) { + const { priority, value } = item; + styleDecl._setProperty(property, value, priority); + } + if (prop === "border") { + styleDecl._updating = false; + styleDecl._notifyChange(originalText); + } +}; diff --git a/lib/handlers/flex.js b/lib/handlers/flex.js new file mode 100644 index 00000000..85055d1c --- /dev/null +++ b/lib/handlers/flex.js @@ -0,0 +1,79 @@ +"use strict"; + +const { shorthandProperties } = require("../normalize"); +const { hasVarFunc, isGlobalKeyword } = require("../parsers"); + +/** + * @param {object} styleDecl - CSSStyleDeclaration instance + * @param {string} prop + * @param {string} val + * @param {string} prior + * @param {string} shorthandProperty + */ +exports.handleFlex = (styleDecl, prop, val, prior, shorthandProperty) => { + if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { + return; + } + let originalText = null; + if (prop === "flex") { + styleDecl._updating = true; + if (typeof styleDecl._onChange === "function") { + originalText = styleDecl.cssText; + } + } + const shorthandPriority = styleDecl._priorities.get(shorthandProperty); + styleDecl.removeProperty(shorthandProperty); + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = styleDecl._priorities.get(prop) ?? ""; + } + styleDecl.removeProperty(prop); + if (shorthandPriority && priority) { + styleDecl._setProperty(prop, val); + } else { + styleDecl._setProperty(prop, val, priority); + } + if (val && !hasVarFunc(val)) { + const longhandValues = []; + const shorthandItem = shorthandProperties.get(shorthandProperty); + let hasGlobalKeywordInValue = false; + for (const [longhandProperty] of shorthandItem.shorthandFor) { + if (longhandProperty === prop) { + if (isGlobalKeyword(val)) { + hasGlobalKeywordInValue = true; + } + longhandValues.push(val); + } else { + const longhandValue = styleDecl.getPropertyValue(longhandProperty); + const longhandPriority = styleDecl._priorities.get(longhandProperty) ?? ""; + if (!longhandValue || longhandPriority !== priority) { + break; + } + if (isGlobalKeyword(longhandValue)) { + hasGlobalKeywordInValue = true; + } + longhandValues.push(longhandValue); + } + } + if (longhandValues.length === shorthandItem.shorthandFor.size) { + if (hasGlobalKeywordInValue) { + const [firstValue, ...restValues] = longhandValues; + if (restValues.every((value) => value === firstValue)) { + styleDecl._setProperty(shorthandProperty, firstValue, priority); + } + } else { + const parsedValue = shorthandItem.parse(longhandValues.join(" ")); + if (parsedValue) { + const shorthandValue = Object.values(parsedValue).join(" "); + styleDecl._setProperty(shorthandProperty, shorthandValue, priority); + } + } + } + } + if (prop === "flex") { + styleDecl._updating = false; + styleDecl._notifyChange(originalText); + } +}; diff --git a/lib/handlers/index.js b/lib/handlers/index.js new file mode 100644 index 00000000..41ef4a6d --- /dev/null +++ b/lib/handlers/index.js @@ -0,0 +1,12 @@ +"use strict"; + +const { handleBorder } = require("./border"); +const { handleFlex } = require("./flex"); +const { handlePositionShorthand, handlePositionLonghand } = require("./position"); + +module.exports = { + handleBorder, + handleFlex, + handlePositionShorthand, + handlePositionLonghand +}; diff --git a/lib/handlers/position.js b/lib/handlers/position.js new file mode 100644 index 00000000..49a20d05 --- /dev/null +++ b/lib/handlers/position.js @@ -0,0 +1,107 @@ +"use strict"; + +const { getPositionValue, shorthandProperties } = require("../normalize"); +const { hasVarFunc } = require("../parsers"); + +/** + * @param {object} styleDecl - CSSStyleDeclaration instance + * @param {string} prop + * @param {Array|string} val + * @param {string} prior + */ +exports.handlePositionShorthand = (styleDecl, prop, val, prior) => { + if (!shorthandProperties.has(prop)) { + return; + } + let originalText = null; + if (prop === "margin" || prop === "padding") { + styleDecl._updating = true; + if (typeof styleDecl._onChange === "function") { + originalText = styleDecl.cssText; + } + } + 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 = styleDecl._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) { + styleDecl._setProperty(longhandProperty, longhandValue, priority); + } else { + const longhandPriority = styleDecl._priorities.get(longhandProperty) ?? ""; + if (longhandPriority) { + hasPriority = true; + } else { + styleDecl._setProperty(longhandProperty, longhandValue, priority); + } + } + } + if (hasPriority) { + styleDecl.removeProperty(prop); + } else { + const shorthandValue = getPositionValue(shorthandValues, position); + styleDecl._setProperty(prop, shorthandValue, priority); + } + if (prop === "margin" || prop === "padding") { + styleDecl._updating = false; + styleDecl._notifyChange(originalText); + } +}; + +/** + * @param {object} styleDecl - CSSStyleDeclaration instance + * @param {string} prop + * @param {string} val + * @param {string} prior + * @param {string} shorthandProperty + */ +exports.handlePositionLonghand = (styleDecl, prop, val, prior, shorthandProperty) => { + if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { + return; + } + const shorthandPriority = styleDecl._priorities.get(shorthandProperty); + styleDecl.removeProperty(shorthandProperty); + let priority = ""; + if (typeof prior === "string") { + priority = prior; + } else { + priority = styleDecl._priorities.get(prop) ?? ""; + } + styleDecl.removeProperty(prop); + if (shorthandPriority && priority) { + styleDecl._setProperty(prop, val); + } else { + styleDecl._setProperty(prop, val, priority); + } + if (val && !hasVarFunc(val)) { + const longhandValues = []; + const { shorthandFor, position: shorthandPosition } = + shorthandProperties.get(shorthandProperty); + for (const [longhandProperty] of shorthandFor) { + const longhandValue = styleDecl.getPropertyValue(longhandProperty); + const longhandPriority = styleDecl._priorities.get(longhandProperty) ?? ""; + if (!longhandValue || longhandPriority !== priority) { + return; + } + longhandValues.push(longhandValue); + } + if (longhandValues.length === shorthandFor.size) { + const replacedValue = getPositionValue(longhandValues, shorthandPosition); + styleDecl._setProperty(shorthandProperty, replacedValue, priority); + } + } +}; diff --git a/lib/utils/constants.js b/lib/utils/constants.js new file mode 100644 index 00000000..e6fb59e1 --- /dev/null +++ b/lib/utils/constants.js @@ -0,0 +1,4 @@ +"use strict"; + +exports.ELEMENT_NODE = 1; +exports.NO_MODIFICATION_ALLOWED_ERR = "NoModificationAllowedError"; From 817f4f2d2bbb2d5493aaa32c6f864928767c2d98 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 23:13:26 +0900 Subject: [PATCH 16/29] Update parsers.js --- lib/parsers.js | 535 +++++++++++++++++++++++------------------ lib/utils/constants.js | 71 ++++++ 2 files changed, 373 insertions(+), 233 deletions(-) diff --git a/lib/parsers.js b/lib/parsers.js index 3a062bc9..0b25cbef 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -8,62 +8,9 @@ const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree"); const csstree = require("css-tree"); const { getCache, setCache } = require("./utils/cache"); const { asciiLowercase } = require("./utils/strings"); - -// CSS global keywords -// @see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords -const GLOBAL_KEY = Object.freeze(["initial", "inherit", "unset", "revert", "revert-layer"]); - -// System colors -// @see https://drafts.csswg.org/css-color/#css-system-colors -// @see https://drafts.csswg.org/css-color/#deprecated-system-colors -const SYS_COLOR = Object.freeze([ - "accentcolor", - "accentcolortext", - "activeborder", - "activecaption", - "activetext", - "appworkspace", - "background", - "buttonborder", - "buttonface", - "buttonhighlight", - "buttonshadow", - "buttontext", - "canvas", - "canvastext", - "captiontext", - "field", - "fieldtext", - "graytext", - "highlight", - "highlighttext", - "inactiveborder", - "inactivecaption", - "inactivecaptiontext", - "infobackground", - "infotext", - "linktext", - "mark", - "marktext", - "menu", - "menutext", - "scrollbar", - "selecteditem", - "selecteditemtext", - "threeddarkshadow", - "threedface", - "threedhighlight", - "threedlightshadow", - "threedshadow", - "visitedtext", - "window", - "windowframe", - "windowtext" -]); +const { CALC_FUNC_NAMES, GLOBAL_KEY, NODE_TYPES, SYS_COLOR } = require("./utils/constants"); // Regular expressions -const CALC_FUNC_NAMES = - "(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)"; const calcRegEx = new RegExp(`^${CALC_FUNC_NAMES}\\(`); const calcContainedRegEx = new RegExp(`(?<=[*/\\s(])${CALC_FUNC_NAMES}\\(`); const calcNameRegEx = new RegExp(`^${CALC_FUNC_NAMES}$`); @@ -73,7 +20,14 @@ const varContainedRegEx = /(?<=[*/\s(])var\(/; // Patched css-tree const cssTree = csstree.fork(syntaxes); -// Prepare stringified value. +/** + * Prepare stringified value. + * + * @param {string|number|null|undefined} value - The value to stringify. + * @param {object} [globalObject=globalThis] - The global object. + * @returns {string} - The stringified value. + * @throws {TypeError} - If the value type cannot be converted to a string. + */ const prepareValue = (value, globalObject = globalThis) => { // `null` is converted to an empty string. // @see https://webidl.spec.whatwg.org/#LegacyNullToEmptyString @@ -100,22 +54,44 @@ const prepareValue = (value, globalObject = globalThis) => { } }; -// Value is a global keyword. +/** + * Check if the value is a global keyword. + * + * @param {string} val - The value to check. + * @returns {boolean} - True if the value is a global keyword, false otherwise. + */ const isGlobalKeyword = (val) => { return GLOBAL_KEY.includes(asciiLowercase(val)); }; -// Value starts with and/or contains CSS var() function. +/** + * Check if the value starts with and/or contains CSS var() function. + * + * @param {string} val - The value to check. + * @returns {boolean} - True if the value contains var(), false otherwise. + */ const hasVarFunc = (val) => { return varRegEx.test(val) || varContainedRegEx.test(val); }; -// Value starts with and/or contains CSS calc() related functions. +/** + * Check if the value starts with and/or contains CSS calc() related functions. + * + * @param {string} val - The value to check. + * @returns {boolean} - True if the value contains calc() related functions, false otherwise. + */ const hasCalcFunc = (val) => { return calcRegEx.test(val) || calcContainedRegEx.test(val); }; -// Parse CSS to AST. +/** + * Parse CSS to AST or plain object. + * + * @param {string} val - The CSS string to parse. + * @param {object} [opt={}] - The options for parsing. + * @param {boolean} [toObject=false] - Whether to return a plain object instead of AST. + * @returns {object} - The parsed AST or plain object. + */ const parseCSS = (val, opt = {}, toObject = false) => { const { globalObject, options } = opt; if (typeof val !== "string") { @@ -128,8 +104,15 @@ const parseCSS = (val, opt = {}, toObject = false) => { return ast; }; -// Value is a valid property value. -// Returns `false` for custom property and/or var(). +/** + * Check if the value is a valid property value. + * Returns `false` for custom property and/or var(). + * + * @param {string} prop - The property name. + * @param {string} val - The property value. + * @param {object} globalObject - The global object. + * @returns {boolean} - True if the value is valid for the property, false otherwise. + */ const isValidPropertyValue = (prop, val, globalObject) => { if (typeof val !== "string") { val = prepareValue(val, globalObject); @@ -146,9 +129,16 @@ const isValidPropertyValue = (prop, val, globalObject) => { } return false; } + + // Use cache for expensive parsing + const cacheKey = `isValidPropertyValue_${prop}_${val}`; + const cachedValue = getCache(cacheKey); + if (typeof cachedValue === "boolean") { + return cachedValue; + } + let ast; try { - // TBD: use cache? ast = parseCSS(val, { globalObject, options: { @@ -156,17 +146,67 @@ const isValidPropertyValue = (prop, val, globalObject) => { } }); } catch { + setCache(cacheKey, false); return false; } const { error, matched } = cssTree.lexer.matchProperty(prop, ast); - return error === null && matched !== null; + const result = error === null && matched !== null; + setCache(cacheKey, result); + return result; }; const defaultOptions = { format: "specifiedValue" }; -// Simplify / resolve math functions. +/** + * Process numeric value. + * + * @param {object} item - The AST node item. + * @param {Array} allowedTypes - The allowed AST node types. + * @param {object} [opt={}] - The options for processing. + * @returns {number|undefined} - The processed number, or undefined if invalid. + */ +const processNumericValue = (item, allowedTypes, opt = {}) => { + const { type, value } = item ?? {}; + + // Allow "Number" with value "0" if "Dimension" or "Percentage" is allowed. + const isZero = type === NODE_TYPES.NUMBER && parseFloat(value) === 0; + const isValidType = + allowedTypes.includes(type) || + (isZero && + (allowedTypes.includes(NODE_TYPES.DIMENSION) || + allowedTypes.includes(NODE_TYPES.PERCENTAGE))); + + if (!isValidType) { + return; + } + + const { clamp } = opt; + const max = opt.max ?? Number.INFINITY; + const min = opt.min ?? Number.NEGATIVE_INFINITY; + let num = parseFloat(value); + + if (clamp) { + if (num > max) { + num = max; + } else if (num < min) { + num = min; + } + } else if (num > max || num < min) { + return; + } + + return num; +}; + +/** + * Simplify / resolve math functions. + * + * @param {string} val - The value to resolve. + * @param {object} [opt={}] - The options for resolution. + * @returns {string|undefined} - The resolved value, or undefined if parsing failed. + */ const resolveCalc = (val, opt = {}) => { const { globalObject, options } = opt; if (typeof val !== "string") { @@ -175,7 +215,14 @@ const resolveCalc = (val, opt = {}) => { if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) { return val; } - // TBD: use cache? + + // Use cache for expensive calculation + const cacheKey = `resolveCalc_${val}`; + const cachedValue = getCache(cacheKey); + if (typeof cachedValue === "string") { + return cachedValue; + } + const obj = parseCSS( val, { @@ -193,7 +240,7 @@ const resolveCalc = (val, opt = {}) => { const values = []; for (const item of items) { const { type: itemType, name: itemName, value: itemValue } = item; - if (itemType === "Function") { + if (itemType === NODE_TYPES.FUNCTION) { const value = cssTree .generate(item) .replace(/\)(?!\)|\s|,)/g, ") ") @@ -205,16 +252,117 @@ const resolveCalc = (val, opt = {}) => { } else { values.push(value); } - } else if (itemType === "String") { + } else if (itemType === NODE_TYPES.STRING) { values.push(`"${itemValue}"`); } else { values.push(itemName ?? itemValue); } } - return values.join(" "); + const result = values.join(" "); + setCache(cacheKey, result); + return result; +}; + +/** + * Create a normalized calc node object. + * + * @param {Array} children - The AST children of the calc function. + * @param {string} value - The normalized string value of the calc function content. + * @param {string} raw - The raw string representation. + * @returns {object} - The normalized calc node object. + */ +const createCalcNode = (children, value, raw) => { + let isNumber = false; + let nodeValue = value; + + // calc(number) の場合のみ特別扱いして数値としてパースする + if (children.length === 1) { + const [child] = children; + if (child.type === NODE_TYPES.NUMBER) { + isNumber = true; + nodeValue = `${parseFloat(child.value)}`; + } + } + + return { + type: "Calc", + name: "calc", + isNumber, + value: nodeValue, + raw + }; }; -// Parse property value. Returns string or array of parsed object. +/** + * Normalize AST children nodes. + * + * @param {Array} items - The AST children nodes. + * @param {string} val - The original value string. + * @param {boolean} caseSensitive - Whether the value is case sensitive. + * @returns {Array} - The normalized AST nodes. + */ +const normalizeAstNodes = (items, val, caseSensitive) => { + const parsedValues = []; + for (const item of items) { + const { children, name, type, value, unit } = item; + switch (type) { + case NODE_TYPES.DIMENSION: { + parsedValues.push({ + type, + value, + unit: asciiLowercase(unit) + }); + break; + } + case NODE_TYPES.FUNCTION: { + const css = cssTree + .generate(item) + .replace(/\)(?!\)|\s|,)/g, ") ") + .trim(); + const raw = items.length === 1 ? val : css; + const itemValue = raw + .replace(new RegExp(`^${name}\\(`), "") + .replace(/\)$/, "") + .trim(); + if (name === "calc") { + parsedValues.push(createCalcNode(children, asciiLowercase(itemValue), raw)); + } else { + parsedValues.push({ + type, + name, + value: asciiLowercase(itemValue), + raw + }); + } + break; + } + case NODE_TYPES.IDENTIFIER: { + if (caseSensitive) { + parsedValues.push(item); + } else { + parsedValues.push({ + type, + name: asciiLowercase(name) + }); + } + break; + } + default: { + parsedValues.push(item); + } + } + } + return parsedValues; +}; + +/** + * Parse property value. Returns string or array of parsed object. + * + * @param {string} prop - The property name. + * @param {string} val - The property value. + * @param {object} [opt={}] - The options for parsing. + * @returns {string|Array|undefined} - The parsed value(s). + */ const parsePropertyValue = (prop, val, opt = {}) => { const { caseSensitive, globalObject, inArray } = opt; val = prepareValue(val, globalObject); @@ -243,7 +391,7 @@ const parsePropertyValue = (prop, val, opt = {}) => { if (inArray) { return [ { - type: "Identifier", + type: NODE_TYPES.IDENTIFIER, name: lowerCasedValue } ]; @@ -273,84 +421,7 @@ const parsePropertyValue = (prop, val, opt = {}) => { } if (inArray) { const obj = cssTree.toPlainObject(ast); - const items = obj.children; - const parsedValues = []; - for (const item of items) { - const { children, name, type, value, unit } = item; - switch (type) { - case "Dimension": { - parsedValues.push({ - type, - value, - unit: asciiLowercase(unit) - }); - break; - } - case "Function": { - const css = cssTree - .generate(item) - .replace(/\)(?!\)|\s|,)/g, ") ") - .trim(); - const raw = items.length === 1 ? val : css; - const itemValue = raw - .replace(new RegExp(`^${name}\\(`), "") - .replace(/\)$/, "") - .trim(); - if (name === "calc") { - if (children.length === 1) { - const [child] = children; - if (child.type === "Number") { - parsedValues.push({ - type: "Calc", - name: "calc", - isNumber: true, - value: `${parseFloat(child.value)}`, - raw - }); - } else { - parsedValues.push({ - type: "Calc", - name: "calc", - isNumber: false, - value: asciiLowercase(itemValue), - raw - }); - } - } else { - parsedValues.push({ - type: "Calc", - name: "calc", - isNumber: false, - value: asciiLowercase(itemValue), - raw - }); - } - } else { - parsedValues.push({ - type, - name, - value: asciiLowercase(itemValue), - raw - }); - } - break; - } - case "Identifier": { - if (caseSensitive) { - parsedValues.push(item); - } else { - parsedValues.push({ - type, - name: asciiLowercase(name) - }); - } - break; - } - default: { - parsedValues.push(item); - } - } - } + const parsedValues = normalizeAstNodes(obj.children, val, caseSensitive); if (cacheKey) { setCache(cacheKey, parsedValues); } @@ -362,49 +433,35 @@ const parsePropertyValue = (prop, val, opt = {}) => { return val; }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @param {object} [opt={}] - The options for parsing. + * @returns {string|undefined} - The parsed number string, or undefined if parsing failed. + */ const parseNumber = (val, opt = {}) => { const [item] = val; - const { type, value } = item ?? {}; - if (type !== "Number") { - return; + const num = processNumericValue(item, [NODE_TYPES.NUMBER], opt); + if (typeof num === "number") { + return `${num}`; } - const { clamp } = opt; - const max = opt.max ?? Number.INFINITY; - const min = opt.min ?? Number.NEGATIVE_INFINITY; - let num = parseFloat(value); - if (clamp) { - if (num > max) { - num = max; - } else if (num < min) { - num = min; - } - } else if (num > max || num < min) { - return; - } - return `${num}`; }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @param {object} [opt={}] - The options for parsing. + * @returns {string|undefined} - The parsed length string, or undefined if parsing failed. + */ const parseLength = (val, opt = {}) => { const [item] = val; - const { type, value, unit } = item ?? {}; - if (type !== "Dimension" && !(type === "Number" && value === "0")) { - return; - } - const { clamp } = opt; - const max = opt.max ?? Number.INFINITY; - const min = opt.min ?? Number.NEGATIVE_INFINITY; - let num = parseFloat(value); - if (clamp) { - if (num > max) { - num = max; - } else if (num < min) { - num = min; - } - } else if (num > max || num < min) { + const num = processNumericValue(item, [NODE_TYPES.DIMENSION], opt); + if (typeof num !== "number") { return; } + const { unit } = item ?? {}; if (num === 0 && !unit) { return `${num}px`; } else if (unit) { @@ -412,72 +469,62 @@ const parseLength = (val, opt = {}) => { } }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @param {object} [opt={}] - The options for parsing. + * @returns {string|undefined} - The parsed percentage string, or undefined if parsing failed. + */ const parsePercentage = (val, opt = {}) => { const [item] = val; - const { type, value } = item ?? {}; - if (type !== "Percentage" && !(type === "Number" && value === "0")) { - return; - } - const { clamp } = opt; - const max = opt.max ?? Number.INFINITY; - const min = opt.min ?? Number.NEGATIVE_INFINITY; - let num = parseFloat(value); - if (clamp) { - if (num > max) { - num = max; - } else if (num < min) { - num = min; - } - } else if (num > max || num < min) { - return; - } - if (num === 0) { + const num = processNumericValue(item, [NODE_TYPES.PERCENTAGE], opt); + if (typeof num === "number") { return `${num}%`; } - return `${num}%`; }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @param {object} [opt={}] - The options for parsing. + * @returns {string|undefined} - The parsed string, or undefined if parsing failed. + */ const parseLengthPercentage = (val, opt = {}) => { const [item] = val; - const { type, value, unit } = item ?? {}; - if (type !== "Dimension" && type !== "Percentage" && !(type === "Number" && value === "0")) { - return; - } - const { clamp } = opt; - const max = opt.max ?? Number.INFINITY; - const min = opt.min ?? Number.NEGATIVE_INFINITY; - let num = parseFloat(value); - if (clamp) { - if (num > max) { - num = max; - } else if (num < min) { - num = min; - } - } else if (num > max || num < min) { + const num = processNumericValue(item, [NODE_TYPES.DIMENSION, NODE_TYPES.PERCENTAGE], opt); + if (typeof num !== "number") { return; } + const { type, unit } = item ?? {}; if (unit) { if (/deg|g?rad|turn/i.test(unit)) { return; } return `${num}${unit}`; - } else if (type === "Percentage") { + } else if (type === NODE_TYPES.PERCENTAGE) { return `${num}%`; } else if (num === 0) { return `${num}px`; } }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @returns {string|undefined} - The parsed angle string, or undefined if parsing failed. + */ const parseAngle = (val) => { const [item] = val; - const { type, value, unit } = item ?? {}; - if (type !== "Dimension" && !(type === "Number" && value === "0")) { + // NOTE: parseAngle signature in source doesn't accept opt, but implementation + // should ideally be consistent. For now, matching existing logic via helper without opts. + const num = processNumericValue(item, [NODE_TYPES.DIMENSION]); + if (typeof num !== "number") { return; } - const num = parseFloat(value); + const { unit } = item ?? {}; if (unit) { if (!/^(?:deg|g?rad|turn)$/i.test(unit)) { return; @@ -488,48 +535,64 @@ const parseAngle = (val) => { } }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @returns {string|undefined} - The parsed URL string, or undefined if parsing failed. + */ const parseURL = (val) => { const [item] = val; const { type, value } = item ?? {}; - if (type !== "Url") { + if (type !== NODE_TYPES.URL) { return; } const str = value.replace(/\\\\/g, "\\").replaceAll('"', '\\"'); return `url("${str}")`; }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @returns {string|undefined} - The parsed string, or undefined if parsing failed. + */ const parseString = (val) => { const [item] = val; const { type, value } = item ?? {}; - if (type !== "String") { + if (type !== NODE_TYPES.STRING) { return; } const str = value.replace(/\\\\/g, "\\").replaceAll('"', '\\"'); return `"${str}"`; }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @param {object} [opt=defaultOptions] - The options for parsing. + * @returns {string|undefined} - The parsed color string, or undefined if parsing failed. + */ const parseColor = (val, opt = defaultOptions) => { const [item] = val; const { name, type, value } = item ?? {}; switch (type) { - case "Function": { + case NODE_TYPES.FUNCTION: { const res = resolveColor(`${name}(${value})`, opt); if (res) { return res; } break; } - case "Hash": { + case NODE_TYPES.HASH: { const res = resolveColor(`#${value}`, opt); if (res) { return res; } break; } - case "Identifier": { + case NODE_TYPES.IDENTIFIER: { if (SYS_COLOR.includes(name)) { return name; } @@ -543,11 +606,17 @@ const parseColor = (val, opt = defaultOptions) => { } }; -// Parse . +/** + * Parse . + * + * @param {Array} val - The parsed AST children. + * @param {object} [opt=defaultOptions] - The options for parsing. + * @returns {string|undefined} - The parsed gradient string, or undefined if parsing failed. + */ const parseGradient = (val, opt = defaultOptions) => { const [item] = val; const { name, type, value } = item ?? {}; - if (type !== "Function") { + if (type !== NODE_TYPES.FUNCTION) { return; } const res = resolveGradient(`${name}(${value})`, opt); diff --git a/lib/utils/constants.js b/lib/utils/constants.js index e6fb59e1..bdcb8b1c 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -1,4 +1,75 @@ "use strict"; +// CSS calc() function names +exports.CALC_FUNC_NAMES = + "(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)"; + +// Node.ELEMENT_NODE exports.ELEMENT_NODE = 1; + +// CSS global keywords +// @see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords +exports.GLOBAL_KEY = Object.freeze(["initial", "inherit", "unset", "revert", "revert-layer"]); + +// AST Node types +exports.NODE_TYPES = Object.freeze({ + DIMENSION: "Dimension", + FUNCTION: "Function", + HASH: "Hash", + IDENTIFIER: "Identifier", + NUMBER: "Number", + PERCENTAGE: "Percentage", + STRING: "String", + URL: "Url" +}); + +// NoModificationAllowedError name exports.NO_MODIFICATION_ALLOWED_ERR = "NoModificationAllowedError"; + +// System colors +// @see https://drafts.csswg.org/css-color/#css-system-colors +// @see https://drafts.csswg.org/css-color/#deprecated-system-colors +exports.SYS_COLOR = Object.freeze([ + "accentcolor", + "accentcolortext", + "activeborder", + "activecaption", + "activetext", + "appworkspace", + "background", + "buttonborder", + "buttonface", + "buttonhighlight", + "buttonshadow", + "buttontext", + "canvas", + "canvastext", + "captiontext", + "field", + "fieldtext", + "graytext", + "highlight", + "highlighttext", + "inactiveborder", + "inactivecaption", + "inactivecaptiontext", + "infobackground", + "infotext", + "linktext", + "mark", + "marktext", + "menu", + "menutext", + "scrollbar", + "selecteditem", + "selecteditemtext", + "threeddarkshadow", + "threedface", + "threedhighlight", + "threedlightshadow", + "threedshadow", + "visitedtext", + "window", + "windowframe", + "windowtext" +]); From a6ea02644a7e2b6b28c0bd00b4b1d0f4c6601d5b Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 23:21:46 +0900 Subject: [PATCH 17/29] Update CSSStyleDeclaration.js --- lib/CSSStyleDeclaration.js | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 4642fb73..62b20ea6 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -12,17 +12,8 @@ const { handlePositionShorthand, handlePositionLonghand } = require("./handlers"); -const { - normalizeProperties, - prepareProperties, - shorthandProperties -} = require("./normalize"); -const { - hasVarFunc, - parseCSS, - parsePropertyValue, - prepareValue -} = require("./parsers"); +const { normalizeProperties, prepareProperties, shorthandProperties } = require("./normalize"); +const { hasVarFunc, parseCSS, parsePropertyValue, prepareValue } = require("./parsers"); const { ELEMENT_NODE, NO_MODIFICATION_ALLOWED_ERR } = require("./utils/constants"); const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); @@ -46,7 +37,7 @@ class CSSStyleDeclaration { * @param {object} globalObject - Window object * @param {object} [opt] - Options * @param {object} [opt.context] - Element or CSSStyleRule - * @param {"specifiedValue"|"computedValue"} [opt.format="specifiedValue"] - "specifiedValue" or "computedValue" + * @param {string} [opt.format="specifiedValue"] - "specifiedValue" or "computedValue" * @param {Function} [opt.onChange] - Callback when cssText is changed or the property is removed */ constructor(globalObject, opt = {}) { @@ -130,10 +121,8 @@ class CSSStyleDeclaration { if (this._updating) { return; } - this._clearProperties(); this._updating = true; - try { const valueObj = this._parseCSSText(val); if (valueObj?.children) { @@ -143,7 +132,6 @@ class CSSStyleDeclaration { } catch { return; } - this._updating = false; this._notifyChange(); } @@ -259,7 +247,6 @@ class CSSStyleDeclaration { const prevValue = this._values.get(property); this._values.delete(property); this._priorities.delete(property); - const removed = this._removeIndex(property); if (removed) { this._notifyChange(); @@ -307,7 +294,7 @@ class CSSStyleDeclaration { * Triggers the onChange callback if it's a function and the text has changed. * * @private - * @param {string|null} [originalText] - The CSS text before the change. If provided, checking compares this with new cssText. + * @param {string|null} [originalText] - The CSS text before the change. */ _notifyChange(originalText = null) { if (typeof this._onChange !== "function") { @@ -339,16 +326,11 @@ class CSSStyleDeclaration { this.removeProperty(property); return; } - // Performance Optimization: - // Only calculate cssText if we are notifying a change AND not currently in a bulk update. - const originalText = (typeof this._onChange === "function" && !this._updating) ? this.cssText : null; - - // Performance Optimization: - // Only scan array to add index if the property is not already known. + const originalText = + typeof this._onChange === "function" && !this._updating ? this.cssText : null; if (!this._values.has(property)) { this._addIndex(property); } - if (priority === "important") { this._priorities.set(property, priority); } else { @@ -539,8 +521,8 @@ for (const definition of propertyDefinitions.values()) { // Assign the internal setters to the prototype. // These methods are used by the properties generated in generated/properties.js. for (const [methodName, handler] of Object.entries(internalSetters)) { - // NOTE: Arrow functions cannot be used here because 'this' must be bound to the CSSStyleDeclaration instance. - CSSStyleDeclaration.prototype[methodName] = function(...args) { + // NOTE: 'this' must be bound to the CSSStyleDeclaration instance. + CSSStyleDeclaration.prototype[methodName] = function (...args) { handler(this, ...args); }; } From a016d1e6b00a77ff0a7552a82d90781d88a0e4fa Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 23:39:34 +0900 Subject: [PATCH 18/29] Update dependencies and devDependencies --- package-lock.json | 39 ++++++++++++++++----------------------- package.json | 6 +++--- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 007b9130..25182ccd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "5.3.3", "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^4.0.5", - "@csstools/css-syntax-patches-for-csstree": "^1.0.16", + "@asamuzakjp/css-color": "^4.1.0", + "@csstools/css-syntax-patches-for-csstree": "^1.0.17", "css-tree": "^3.1.0", "lru-cache": "^11.2.2" }, @@ -20,7 +20,7 @@ "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^8.0.1", + "@webref/css": "^8.0.2", "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", @@ -34,16 +34,16 @@ } }, "node_modules/@asamuzakjp/css-color": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz", - "integrity": "sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.0.tgz", + "integrity": "sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==", "license": "MIT", "dependencies": { "@csstools/css-calc": "^2.1.4", "@csstools/css-color-parser": "^3.1.0", "@csstools/css-parser-algorithms": "^3.0.5", "@csstools/css-tokenizer": "^3.0.4", - "lru-cache": "^11.2.1" + "lru-cache": "^11.2.2" } }, "node_modules/@babel/code-frame": { @@ -256,7 +256,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -265,9 +264,9 @@ } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.16.tgz", - "integrity": "sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg==", + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.17.tgz", + "integrity": "sha512-LCC++2h8pLUSPY+EsZmrrJ1EOUu+5iClpEiDhhdw3zRJpPbABML/N5lmRuBHjxtKm9VnRcsUzioyD0sekFMF0A==", "funding": [ { "type": "github", @@ -298,7 +297,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -603,9 +601,9 @@ "license": "MIT" }, "node_modules/@webref/css": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@webref/css/-/css-8.0.1.tgz", - "integrity": "sha512-TxryLLjDGJnNbv55arQ1v3YAzmuniXARJ5RaBLMIA1Ijx/uICUQ0GCSvGem997K4LsMW+UztrXPpxmsop9JcdA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@webref/css/-/css-8.0.2.tgz", + "integrity": "sha512-rYazvgX2Xe54ap29GHeA77Z/s8jCoStIjsZIpOAwJfaktudtxG7P+p54rbAGy63cMkknIeiFS5YYOJQ3M03aaw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -618,7 +616,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -768,7 +765,6 @@ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "license": "MIT", - "peer": true, "dependencies": { "mdn-data": "2.12.2", "source-map-js": "^1.0.1" @@ -821,7 +817,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -882,7 +877,6 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", - "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -1247,9 +1241,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -1616,7 +1610,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, diff --git a/package.json b/package.json index 6671eebe..28a5824a 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ ], "main": "./lib/index.js", "dependencies": { - "@asamuzakjp/css-color": "^4.0.5", - "@csstools/css-syntax-patches-for-csstree": "^1.0.16", + "@asamuzakjp/css-color": "^4.1.0", + "@csstools/css-syntax-patches-for-csstree": "^1.0.17", "css-tree": "^3.1.0", "lru-cache": "^11.2.2" }, @@ -48,7 +48,7 @@ "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@domenic/eslint-config": "^4.0.1", - "@webref/css": "^8.0.1", + "@webref/css": "^8.0.2", "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", From d53baf36262b6fbe89fe6cc3f0be255dc9471426 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 22 Nov 2025 23:51:07 +0900 Subject: [PATCH 19/29] Update JSDoc comments --- lib/handlers/border.js | 2 ++ lib/handlers/flex.js | 2 ++ lib/handlers/position.js | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/lib/handlers/border.js b/lib/handlers/border.js index 08b832c5..4bf9beb8 100644 --- a/lib/handlers/border.js +++ b/lib/handlers/border.js @@ -3,6 +3,8 @@ const { borderProperties, prepareBorderProperties } = require("../normalize"); /** + * Handles border shorthand properties. + * * @param {object} styleDecl - CSSStyleDeclaration instance * @param {string} prop * @param {object|Array|string} val diff --git a/lib/handlers/flex.js b/lib/handlers/flex.js index 85055d1c..f6280a5a 100644 --- a/lib/handlers/flex.js +++ b/lib/handlers/flex.js @@ -4,6 +4,8 @@ const { shorthandProperties } = require("../normalize"); const { hasVarFunc, isGlobalKeyword } = require("../parsers"); /** + * Handles flex shorthand property. + * * @param {object} styleDecl - CSSStyleDeclaration instance * @param {string} prop * @param {string} val diff --git a/lib/handlers/position.js b/lib/handlers/position.js index 49a20d05..03e31547 100644 --- a/lib/handlers/position.js +++ b/lib/handlers/position.js @@ -4,6 +4,8 @@ const { getPositionValue, shorthandProperties } = require("../normalize"); const { hasVarFunc } = require("../parsers"); /** + * Handles position shorthand properties. + * * @param {object} styleDecl - CSSStyleDeclaration instance * @param {string} prop * @param {Array|string} val @@ -63,6 +65,8 @@ exports.handlePositionShorthand = (styleDecl, prop, val, prior) => { }; /** + * Handles position longhand properties. + * * @param {object} styleDecl - CSSStyleDeclaration instance * @param {string} prop * @param {string} val From 60c6d16746af45557cd04ffffb372b7a717eeb07 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sun, 23 Nov 2025 06:37:18 +0900 Subject: [PATCH 20/29] Update parsers.js --- lib/parsers.js | 194 +++++++++++++++++++---------------------- lib/utils/constants.js | 31 ++++--- 2 files changed, 109 insertions(+), 116 deletions(-) diff --git a/lib/parsers.js b/lib/parsers.js index 0b25cbef..69a2d96d 100644 --- a/lib/parsers.js +++ b/lib/parsers.js @@ -7,8 +7,14 @@ const { const { next: syntaxes } = require("@csstools/css-syntax-patches-for-csstree"); const csstree = require("css-tree"); const { getCache, setCache } = require("./utils/cache"); +const { + AST_TYPES, + CALC_FUNC_NAMES, + DEFAULT_OPTS, + GLOBAL_KEYS, + SYS_COLORS +} = require("./utils/constants"); const { asciiLowercase } = require("./utils/strings"); -const { CALC_FUNC_NAMES, GLOBAL_KEY, NODE_TYPES, SYS_COLOR } = require("./utils/constants"); // Regular expressions const calcRegEx = new RegExp(`^${CALC_FUNC_NAMES}\\(`); @@ -60,9 +66,7 @@ const prepareValue = (value, globalObject = globalThis) => { * @param {string} val - The value to check. * @returns {boolean} - True if the value is a global keyword, false otherwise. */ -const isGlobalKeyword = (val) => { - return GLOBAL_KEY.includes(asciiLowercase(val)); -}; +const isGlobalKeyword = (val) => GLOBAL_KEYS.includes(asciiLowercase(val)); /** * Check if the value starts with and/or contains CSS var() function. @@ -70,9 +74,7 @@ const isGlobalKeyword = (val) => { * @param {string} val - The value to check. * @returns {boolean} - True if the value contains var(), false otherwise. */ -const hasVarFunc = (val) => { - return varRegEx.test(val) || varContainedRegEx.test(val); -}; +const hasVarFunc = (val) => varRegEx.test(val) || varContainedRegEx.test(val); /** * Check if the value starts with and/or contains CSS calc() related functions. @@ -80,9 +82,7 @@ const hasVarFunc = (val) => { * @param {string} val - The value to check. * @returns {boolean} - True if the value contains calc() related functions, false otherwise. */ -const hasCalcFunc = (val) => { - return calcRegEx.test(val) || calcContainedRegEx.test(val); -}; +const hasCalcFunc = (val) => calcRegEx.test(val) || calcContainedRegEx.test(val); /** * Parse CSS to AST or plain object. @@ -123,42 +123,34 @@ const isValidPropertyValue = (prop, val, globalObject) => { // cssTree.lexer does not support deprecated system colors // @see https://github.com/w3c/webref/issues/1519#issuecomment-3120290261 // @see https://github.com/w3c/webref/issues/1647 - if (SYS_COLOR.includes(asciiLowercase(val))) { + if (SYS_COLORS.includes(asciiLowercase(val))) { if (/^(?:-webkit-)?(?:[a-z][a-z\d]*-)*color$/i.test(prop)) { return true; } return false; } - - // Use cache for expensive parsing const cacheKey = `isValidPropertyValue_${prop}_${val}`; const cachedValue = getCache(cacheKey); if (typeof cachedValue === "boolean") { return cachedValue; } - - let ast; + let result; try { - ast = parseCSS(val, { + const ast = parseCSS(val, { globalObject, options: { context: "value" } }); + const { error, matched } = cssTree.lexer.matchProperty(prop, ast); + result = error === null && matched !== null; } catch { - setCache(cacheKey, false); - return false; + result = false; } - const { error, matched } = cssTree.lexer.matchProperty(prop, ast); - const result = error === null && matched !== null; setCache(cacheKey, result); return result; }; -const defaultOptions = { - format: "specifiedValue" -}; - /** * Process numeric value. * @@ -169,24 +161,19 @@ const defaultOptions = { */ const processNumericValue = (item, allowedTypes, opt = {}) => { const { type, value } = item ?? {}; - // Allow "Number" with value "0" if "Dimension" or "Percentage" is allowed. - const isZero = type === NODE_TYPES.NUMBER && parseFloat(value) === 0; + const isZero = type === AST_TYPES.NUMBER && parseFloat(value) === 0; const isValidType = allowedTypes.includes(type) || (isZero && - (allowedTypes.includes(NODE_TYPES.DIMENSION) || - allowedTypes.includes(NODE_TYPES.PERCENTAGE))); - + (allowedTypes.includes(AST_TYPES.DIMENSION) || allowedTypes.includes(AST_TYPES.PERCENTAGE))); if (!isValidType) { return; } - const { clamp } = opt; const max = opt.max ?? Number.INFINITY; const min = opt.min ?? Number.NEGATIVE_INFINITY; let num = parseFloat(value); - if (clamp) { if (num > max) { num = max; @@ -196,7 +183,6 @@ const processNumericValue = (item, allowedTypes, opt = {}) => { } else if (num > max || num < min) { return; } - return num; }; @@ -215,14 +201,11 @@ const resolveCalc = (val, opt = {}) => { if (val === "" || hasVarFunc(val) || !hasCalcFunc(val)) { return val; } - - // Use cache for expensive calculation const cacheKey = `resolveCalc_${val}`; const cachedValue = getCache(cacheKey); if (typeof cachedValue === "string") { return cachedValue; } - const obj = parseCSS( val, { @@ -240,19 +223,19 @@ const resolveCalc = (val, opt = {}) => { const values = []; for (const item of items) { const { type: itemType, name: itemName, value: itemValue } = item; - if (itemType === NODE_TYPES.FUNCTION) { + if (itemType === AST_TYPES.FUNCTION) { const value = cssTree .generate(item) .replace(/\)(?!\)|\s|,)/g, ") ") .replace(/,(?!\s)/g, ", ") .trim(); if (calcNameRegEx.test(itemName)) { - const newValue = cssCalc(value, options ?? defaultOptions); + const newValue = cssCalc(value, options ?? DEFAULT_OPTS); values.push(newValue); } else { values.push(value); } - } else if (itemType === NODE_TYPES.STRING) { + } else if (itemType === AST_TYPES.STRING) { values.push(`"${itemValue}"`); } else { values.push(itemName ?? itemValue); @@ -274,16 +257,13 @@ const resolveCalc = (val, opt = {}) => { const createCalcNode = (children, value, raw) => { let isNumber = false; let nodeValue = value; - - // calc(number) の場合のみ特別扱いして数値としてパースする if (children.length === 1) { const [child] = children; - if (child.type === NODE_TYPES.NUMBER) { + if (child.type === AST_TYPES.NUMBER) { isNumber = true; nodeValue = `${parseFloat(child.value)}`; } } - return { type: "Calc", name: "calc", @@ -306,7 +286,7 @@ const normalizeAstNodes = (items, val, caseSensitive) => { for (const item of items) { const { children, name, type, value, unit } = item; switch (type) { - case NODE_TYPES.DIMENSION: { + case AST_TYPES.DIMENSION: { parsedValues.push({ type, value, @@ -314,7 +294,7 @@ const normalizeAstNodes = (items, val, caseSensitive) => { }); break; } - case NODE_TYPES.FUNCTION: { + case AST_TYPES.FUNCTION: { const css = cssTree .generate(item) .replace(/\)(?!\)|\s|,)/g, ") ") @@ -336,7 +316,7 @@ const normalizeAstNodes = (items, val, caseSensitive) => { } break; } - case NODE_TYPES.IDENTIFIER: { + case AST_TYPES.IDENTIFIER: { if (caseSensitive) { parsedValues.push(item); } else { @@ -375,62 +355,71 @@ const parsePropertyValue = (prop, val, opt = {}) => { } val = calculatedValue; } + const cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`; + const cachedValue = getCache(cacheKey); + if (cachedValue === false) { + return; + } else if (inArray) { + if (Array.isArray(cachedValue)) { + return cachedValue; + } + } else if (typeof cachedValue === "string") { + return cachedValue; + } + let parsedValue; const lowerCasedValue = asciiLowercase(val); - if (GLOBAL_KEY.includes(lowerCasedValue)) { + if (GLOBAL_KEYS.includes(lowerCasedValue)) { if (inArray) { - return [ + parsedValue = [ { type: "GlobalKeyword", name: lowerCasedValue } ]; + } else { + parsedValue = lowerCasedValue; } - return lowerCasedValue; - } else if (SYS_COLOR.includes(lowerCasedValue)) { + } else if (SYS_COLORS.includes(lowerCasedValue)) { if (/^(?:(?:-webkit-)?(?:[a-z][a-z\d]*-)*color|border)$/i.test(prop)) { if (inArray) { - return [ + parsedValue = [ { - type: NODE_TYPES.IDENTIFIER, + type: AST_TYPES.IDENTIFIER, name: lowerCasedValue } ]; + } else { + parsedValue = lowerCasedValue; } - return lowerCasedValue; - } - return; - } - try { - let cacheKey = ""; - if (inArray) { - cacheKey = `parsePropertyValue_${prop}_${val}_${caseSensitive}`; - const cachedValues = getCache(cacheKey); - if (Array.isArray(cachedValues)) { - return cachedValues; - } - } - const ast = parseCSS(val, { - globalObject, - options: { - context: "value" - } - }); - const { error, matched } = cssTree.lexer.matchProperty(prop, ast); - if (error || !matched) { - return; + } else { + parsedValue = false; } - if (inArray) { - const obj = cssTree.toPlainObject(ast); - const parsedValues = normalizeAstNodes(obj.children, val, caseSensitive); - if (cacheKey) { - setCache(cacheKey, parsedValues); + } else { + try { + const ast = parseCSS(val, { + globalObject, + options: { + context: "value" + } + }); + const { error, matched } = cssTree.lexer.matchProperty(prop, ast); + if (error || !matched) { + parsedValue = false; + } else if (inArray) { + const obj = cssTree.toPlainObject(ast); + parsedValue = normalizeAstNodes(obj.children, val, caseSensitive); + } else { + parsedValue = val; } - return parsedValues; + } catch { + parsedValue = false; } - } catch { + } + setCache(cacheKey, parsedValue); + if (parsedValue === false) { return; } - return val; + return parsedValue; }; /** @@ -442,7 +431,7 @@ const parsePropertyValue = (prop, val, opt = {}) => { */ const parseNumber = (val, opt = {}) => { const [item] = val; - const num = processNumericValue(item, [NODE_TYPES.NUMBER], opt); + const num = processNumericValue(item, [AST_TYPES.NUMBER], opt); if (typeof num === "number") { return `${num}`; } @@ -457,7 +446,7 @@ const parseNumber = (val, opt = {}) => { */ const parseLength = (val, opt = {}) => { const [item] = val; - const num = processNumericValue(item, [NODE_TYPES.DIMENSION], opt); + const num = processNumericValue(item, [AST_TYPES.DIMENSION], opt); if (typeof num !== "number") { return; } @@ -478,7 +467,7 @@ const parseLength = (val, opt = {}) => { */ const parsePercentage = (val, opt = {}) => { const [item] = val; - const num = processNumericValue(item, [NODE_TYPES.PERCENTAGE], opt); + const num = processNumericValue(item, [AST_TYPES.PERCENTAGE], opt); if (typeof num === "number") { return `${num}%`; } @@ -493,7 +482,7 @@ const parsePercentage = (val, opt = {}) => { */ const parseLengthPercentage = (val, opt = {}) => { const [item] = val; - const num = processNumericValue(item, [NODE_TYPES.DIMENSION, NODE_TYPES.PERCENTAGE], opt); + const num = processNumericValue(item, [AST_TYPES.DIMENSION, AST_TYPES.PERCENTAGE], opt); if (typeof num !== "number") { return; } @@ -503,7 +492,7 @@ const parseLengthPercentage = (val, opt = {}) => { return; } return `${num}${unit}`; - } else if (type === NODE_TYPES.PERCENTAGE) { + } else if (type === AST_TYPES.PERCENTAGE) { return `${num}%`; } else if (num === 0) { return `${num}px`; @@ -514,13 +503,12 @@ const parseLengthPercentage = (val, opt = {}) => { * Parse . * * @param {Array} val - The parsed AST children. + * @param {object} [opt={}] - The options for parsing. * @returns {string|undefined} - The parsed angle string, or undefined if parsing failed. */ -const parseAngle = (val) => { +const parseAngle = (val, opt = {}) => { const [item] = val; - // NOTE: parseAngle signature in source doesn't accept opt, but implementation - // should ideally be consistent. For now, matching existing logic via helper without opts. - const num = processNumericValue(item, [NODE_TYPES.DIMENSION]); + const num = processNumericValue(item, [AST_TYPES.DIMENSION], opt); if (typeof num !== "number") { return; } @@ -544,7 +532,7 @@ const parseAngle = (val) => { const parseURL = (val) => { const [item] = val; const { type, value } = item ?? {}; - if (type !== NODE_TYPES.URL) { + if (type !== AST_TYPES.URL) { return; } const str = value.replace(/\\\\/g, "\\").replaceAll('"', '\\"'); @@ -560,7 +548,7 @@ const parseURL = (val) => { const parseString = (val) => { const [item] = val; const { type, value } = item ?? {}; - if (type !== NODE_TYPES.STRING) { + if (type !== AST_TYPES.STRING) { return; } const str = value.replace(/\\\\/g, "\\").replaceAll('"', '\\"'); @@ -571,29 +559,29 @@ const parseString = (val) => { * Parse . * * @param {Array} val - The parsed AST children. - * @param {object} [opt=defaultOptions] - The options for parsing. + * @param {object} [opt] - The options for parsing. * @returns {string|undefined} - The parsed color string, or undefined if parsing failed. */ -const parseColor = (val, opt = defaultOptions) => { +const parseColor = (val, opt) => { const [item] = val; const { name, type, value } = item ?? {}; switch (type) { - case NODE_TYPES.FUNCTION: { - const res = resolveColor(`${name}(${value})`, opt); + case AST_TYPES.FUNCTION: { + const res = resolveColor(`${name}(${value})`, opt ?? DEFAULT_OPTS); if (res) { return res; } break; } - case NODE_TYPES.HASH: { + case AST_TYPES.HASH: { const res = resolveColor(`#${value}`, opt); if (res) { return res; } break; } - case NODE_TYPES.IDENTIFIER: { - if (SYS_COLOR.includes(name)) { + case AST_TYPES.IDENTIFIER: { + if (SYS_COLORS.includes(name)) { return name; } const res = resolveColor(name, opt); @@ -610,16 +598,16 @@ const parseColor = (val, opt = defaultOptions) => { * Parse . * * @param {Array} val - The parsed AST children. - * @param {object} [opt=defaultOptions] - The options for parsing. + * @param {object} [opt] - The options for parsing. * @returns {string|undefined} - The parsed gradient string, or undefined if parsing failed. */ -const parseGradient = (val, opt = defaultOptions) => { +const parseGradient = (val, opt) => { const [item] = val; const { name, type, value } = item ?? {}; - if (type !== NODE_TYPES.FUNCTION) { + if (type !== AST_TYPES.FUNCTION) { return; } - const res = resolveGradient(`${name}(${value})`, opt); + const res = resolveGradient(`${name}(${value})`, opt ?? DEFAULT_OPTS); if (res) { return res; } diff --git a/lib/utils/constants.js b/lib/utils/constants.js index bdcb8b1c..06c1aff5 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -1,18 +1,7 @@ "use strict"; -// CSS calc() function names -exports.CALC_FUNC_NAMES = - "(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)"; - -// Node.ELEMENT_NODE -exports.ELEMENT_NODE = 1; - -// CSS global keywords -// @see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords -exports.GLOBAL_KEY = Object.freeze(["initial", "inherit", "unset", "revert", "revert-layer"]); - // AST Node types -exports.NODE_TYPES = Object.freeze({ +exports.AST_TYPES = Object.freeze({ DIMENSION: "Dimension", FUNCTION: "Function", HASH: "Hash", @@ -23,13 +12,29 @@ exports.NODE_TYPES = Object.freeze({ URL: "Url" }); +// CSS calc() function names +exports.CALC_FUNC_NAMES = + "(?:a?(?:cos|sin|tan)|abs|atan2|calc|clamp|exp|hypot|log|max|min|mod|pow|rem|round|sign|sqrt)"; + +// Default options +exports.DEFAULT_OPTS = { + format: "specifiedValue" +}; + +// Node.ELEMENT_NODE +exports.ELEMENT_NODE = 1; + +// CSS global keywords +// @see https://drafts.csswg.org/css-cascade-5/#defaulting-keywords +exports.GLOBAL_KEYS = Object.freeze(["initial", "inherit", "unset", "revert", "revert-layer"]); + // NoModificationAllowedError name exports.NO_MODIFICATION_ALLOWED_ERR = "NoModificationAllowedError"; // System colors // @see https://drafts.csswg.org/css-color/#css-system-colors // @see https://drafts.csswg.org/css-color/#deprecated-system-colors -exports.SYS_COLOR = Object.freeze([ +exports.SYS_COLORS = Object.freeze([ "accentcolor", "accentcolortext", "activeborder", From fc7f5aa859d1abd07d189d3597446b98f1874862 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sun, 23 Nov 2025 18:19:08 +0900 Subject: [PATCH 21/29] Update CSSStyleDeclaration.js --- lib/CSSStyleDeclaration.js | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 62b20ea6..20ea8357 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -14,19 +14,11 @@ const { } = require("./handlers"); const { normalizeProperties, prepareProperties, shorthandProperties } = require("./normalize"); const { hasVarFunc, parseCSS, parsePropertyValue, prepareValue } = require("./parsers"); +const { getCache, setCache } = require("./utils/cache"); const { ELEMENT_NODE, NO_MODIFICATION_ALLOWED_ERR } = require("./utils/constants"); const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); -// Internal Setters (Dynamic Assignment) -// These handlers are mapped to internal method names expected by generated properties. -const internalSetters = { - _borderSetter: handleBorder, - _flexBoxSetter: handleFlex, - _positionShorthandSetter: handlePositionShorthand, - _positionLonghandSetter: handlePositionLonghand -}; - /** * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface */ @@ -389,8 +381,12 @@ class CSSStyleDeclaration { * @returns {object} The parsed CSS object. */ _parseCSSText(val) { - // TBD: use cache? - return parseCSS( + const cacheKey = `parseCSSText_${val}`; + const cachedValue = getCache(cacheKey); + if (cachedValue && Object.hasOwn(cachedValue, "children")) { + return cachedValue; + } + const parsedValue = parseCSS( val, { globalObject: this._global, @@ -401,6 +397,8 @@ class CSSStyleDeclaration { }, true ); + setCache(cacheKey, parsedValue); + return parsedValue; } /** @@ -451,13 +449,12 @@ class CSSStyleDeclaration { if (isCustomProperty || hasVarFunc(value)) { this._addPropertyToMap(properties, property, value, priority); } else { - // TBD: use cache? const parsedValue = parsePropertyValue(property, value, { globalObject: this._global, options: this._options }); if (parsedValue) { - this._addPropertyToMap(properties, property, value, priority); + this._addPropertyToMap(properties, property, parsedValue, priority); } else { this.removeProperty(property); } @@ -518,8 +515,15 @@ for (const definition of propertyDefinitions.values()) { } } +// These handlers are mapped to internal method names expected by generated properties. +const internalSetters = { + _borderSetter: handleBorder, + _flexBoxSetter: handleFlex, + _positionShorthandSetter: handlePositionShorthand, + _positionLonghandSetter: handlePositionLonghand +}; + // Assign the internal setters to the prototype. -// These methods are used by the properties generated in generated/properties.js. for (const [methodName, handler] of Object.entries(internalSetters)) { // NOTE: 'this' must be bound to the CSSStyleDeclaration instance. CSSStyleDeclaration.prototype[methodName] = function (...args) { From 36e5e5d042a88114d7c9cdd0de1acae278f541a7 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 24 Nov 2025 11:01:39 +0900 Subject: [PATCH 22/29] Update properties --- lib/properties/background.js | 168 +++++++++++----------- lib/properties/backgroundAttachment.js | 26 ++-- lib/properties/backgroundClip.js | 26 ++-- lib/properties/backgroundColor.js | 24 ++-- lib/properties/backgroundImage.js | 28 ++-- lib/properties/backgroundOrigin.js | 26 ++-- lib/properties/backgroundPosition.js | 93 ++++++------ lib/properties/backgroundRepeat.js | 28 ++-- lib/properties/backgroundSize.js | 44 +++--- lib/properties/border.js | 85 +++++------ lib/properties/borderBottom.js | 89 ++++++------ lib/properties/borderBottomColor.js | 30 ++-- lib/properties/borderBottomStyle.js | 32 +++-- lib/properties/borderBottomWidth.js | 34 ++--- lib/properties/borderCollapse.js | 20 +-- lib/properties/borderColor.js | 43 +++--- lib/properties/borderLeft.js | 89 ++++++------ lib/properties/borderLeftColor.js | 30 ++-- lib/properties/borderLeftStyle.js | 32 +++-- lib/properties/borderLeftWidth.js | 34 ++--- lib/properties/borderRight.js | 89 ++++++------ lib/properties/borderRightColor.js | 30 ++-- lib/properties/borderRightStyle.js | 32 +++-- lib/properties/borderRightWidth.js | 34 ++--- lib/properties/borderSpacing.js | 28 ++-- lib/properties/borderStyle.js | 45 +++--- lib/properties/borderTop.js | 89 ++++++------ lib/properties/borderTopColor.js | 30 ++-- lib/properties/borderTopStyle.js | 32 +++-- lib/properties/borderTopWidth.js | 34 ++--- lib/properties/borderWidth.js | 47 +++--- lib/properties/bottom.js | 30 ++-- lib/properties/clear.js | 20 +-- lib/properties/clip.js | 22 +-- lib/properties/color.js | 18 +-- lib/properties/display.js | 140 +++++++++--------- lib/properties/flex.js | 123 ++++++++-------- lib/properties/flexBasis.js | 28 ++-- lib/properties/flexGrow.js | 26 ++-- lib/properties/flexShrink.js | 26 ++-- lib/properties/float.js | 20 +-- lib/properties/floodColor.js | 18 +-- lib/properties/font.js | 88 ++++++------ lib/properties/fontFamily.js | 32 +++-- lib/properties/fontSize.js | 28 ++-- lib/properties/fontStyle.js | 35 ++--- lib/properties/fontVariant.js | 33 +++-- lib/properties/fontWeight.js | 28 ++-- lib/properties/height.js | 22 +-- lib/properties/left.js | 22 +-- lib/properties/lightingColor.js | 18 +-- lib/properties/lineHeight.js | 30 ++-- lib/properties/margin.js | 72 +++++++--- lib/properties/marginBottom.js | 28 ++-- lib/properties/marginLeft.js | 28 ++-- lib/properties/marginRight.js | 28 ++-- lib/properties/marginTop.js | 28 ++-- lib/properties/opacity.js | 26 ++-- lib/properties/outlineColor.js | 18 +-- lib/properties/padding.js | 70 +++++++-- lib/properties/paddingBottom.js | 26 ++-- lib/properties/paddingLeft.js | 26 ++-- lib/properties/paddingRight.js | 26 ++-- lib/properties/paddingTop.js | 26 ++-- lib/properties/right.js | 22 +-- lib/properties/stopColor.js | 18 +-- lib/properties/top.js | 22 +-- lib/properties/webkitBorderAfterColor.js | 18 +-- lib/properties/webkitBorderBeforeColor.js | 18 +-- lib/properties/webkitBorderEndColor.js | 18 +-- lib/properties/webkitBorderStartColor.js | 18 +-- lib/properties/webkitColumnRuleColor.js | 18 +-- lib/properties/webkitTapHighlightColor.js | 18 +-- lib/properties/webkitTextEmphasisColor.js | 18 +-- lib/properties/webkitTextFillColor.js | 18 +-- lib/properties/webkitTextStrokeColor.js | 18 +-- lib/properties/width.js | 22 +-- lib/utils/constants.js | 3 + lib/utils/propertyDescriptors.js | 20 +-- test/CSSStyleDeclaration.test.js | 18 +++ 80 files changed, 1633 insertions(+), 1334 deletions(-) diff --git a/lib/properties/background.js b/lib/properties/background.js index 8d2eb2d9..b045264c 100644 --- a/lib/properties/background.js +++ b/lib/properties/background.js @@ -10,28 +10,36 @@ const backgroundClip = require("./backgroundClip"); const backgroundAttachment = require("./backgroundAttachment"); const backgroundColor = require("./backgroundColor"); -const property = "background"; +const PROPERTY = "background"; +const BG_ATTACH = "background-attachment"; +const BG_CLIP = "background-clip"; +const BG_COLOR = "background-color"; +const BG_IMAGE = "background-image"; +const BG_ORIGIN = "background-origin"; +const BG_POSITION = "background-position"; +const BG_REPEAT = "background-repeat"; +const BG_SIZE = "background-size"; module.exports.initialValues = new Map([ - ["background-image", "none"], - ["background-position", "0% 0%"], - ["background-size", "auto"], - ["background-repeat", "repeat"], - ["background-origin", "padding-box"], - ["background-clip", "border-box"], - ["background-attachment", "scroll"], - ["background-color", "transparent"] + [BG_IMAGE, "none"], + [BG_POSITION, "0% 0%"], + [BG_SIZE, "auto"], + [BG_REPEAT, "repeat"], + [BG_ORIGIN, "padding-box"], + [BG_CLIP, "border-box"], + [BG_ATTACH, "scroll"], + [BG_COLOR, "transparent"] ]); module.exports.shorthandFor = new Map([ - ["background-image", backgroundImage], - ["background-position", backgroundPosition], - ["background-size", backgroundSize], - ["background-repeat", backgroundRepeat], - ["background-origin", backgroundOrigin], - ["background-clip", backgroundClip], - ["background-attachment", backgroundAttachment], - ["background-color", backgroundColor] + [BG_IMAGE, backgroundImage], + [BG_POSITION, backgroundPosition], + [BG_SIZE, backgroundSize], + [BG_REPEAT, backgroundRepeat], + [BG_ORIGIN, backgroundOrigin], + [BG_CLIP, backgroundClip], + [BG_ATTACH, backgroundAttachment], + [BG_COLOR, backgroundColor] ]); module.exports.parse = function parse(v, opt = {}) { @@ -41,7 +49,7 @@ module.exports.parse = function parse(v, opt = {}) { } else if (parsers.hasCalcFunc(v)) { v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v, globalObject)) { + if (!parsers.isValidPropertyValue(PROPERTY, v, globalObject)) { return; } const values = parsers.splitValue(v, { @@ -51,24 +59,24 @@ module.exports.parse = function parse(v, opt = {}) { const l = values.length; for (let i = 0; i < l; i++) { let bg = { - "background-image": module.exports.initialValues.get("background-image"), - "background-position": module.exports.initialValues.get("background-position"), - "background-size": module.exports.initialValues.get("background-size"), - "background-repeat": module.exports.initialValues.get("background-repeat"), - "background-origin": module.exports.initialValues.get("background-origin"), - "background-clip": module.exports.initialValues.get("background-clip"), - "background-attachment": module.exports.initialValues.get("background-attachment"), - "background-color": module.exports.initialValues.get("background-color") + [BG_IMAGE]: module.exports.initialValues.get(BG_IMAGE), + [BG_POSITION]: module.exports.initialValues.get(BG_POSITION), + [BG_SIZE]: module.exports.initialValues.get(BG_SIZE), + [BG_REPEAT]: module.exports.initialValues.get(BG_REPEAT), + [BG_ORIGIN]: module.exports.initialValues.get(BG_ORIGIN), + [BG_CLIP]: module.exports.initialValues.get(BG_CLIP), + [BG_ATTACH]: module.exports.initialValues.get(BG_ATTACH), + [BG_COLOR]: module.exports.initialValues.get(BG_COLOR) }; if (l > 1 && i !== l - 1) { bg = { - "background-image": module.exports.initialValues.get("background-image"), - "background-position": module.exports.initialValues.get("background-position"), - "background-size": module.exports.initialValues.get("background-size"), - "background-repeat": module.exports.initialValues.get("background-repeat"), - "background-origin": module.exports.initialValues.get("background-origin"), - "background-clip": module.exports.initialValues.get("background-clip"), - "background-attachment": module.exports.initialValues.get("background-attachment") + [BG_IMAGE]: module.exports.initialValues.get(BG_IMAGE), + [BG_POSITION]: module.exports.initialValues.get(BG_POSITION), + [BG_SIZE]: module.exports.initialValues.get(BG_SIZE), + [BG_REPEAT]: module.exports.initialValues.get(BG_REPEAT), + [BG_ORIGIN]: module.exports.initialValues.get(BG_ORIGIN), + [BG_CLIP]: module.exports.initialValues.get(BG_CLIP), + [BG_ATTACH]: module.exports.initialValues.get(BG_ATTACH) }; } const bgPosition = []; @@ -89,15 +97,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsers.isValidPropertyValue(longhand, part, globalObject)) { partValid = true; switch (longhand) { - case "background-clip": - case "background-origin": { + case BG_CLIP: + case BG_ORIGIN: { const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgBox.push(parsedValue); } break; } - case "background-color": { + case BG_COLOR: { if (i !== values.length - 1) { return; } @@ -107,15 +115,15 @@ module.exports.parse = function parse(v, opt = {}) { } break; } - case "background-position": { + case BG_POSITION: { bgPosition.push(part); break; } - case "background-repeat": { + case BG_REPEAT: { bgRepeat.push(part); break; } - case "background-size": { + case BG_SIZE: { break; } default: { @@ -139,15 +147,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsers.isValidPropertyValue(longhand, part, globalObject)) { partValid = true; switch (longhand) { - case "background-clip": - case "background-origin": { + case BG_CLIP: + case BG_ORIGIN: { const parsedValue = value.parse(part, { globalObject, options }); if (parsedValue) { bgBox.push(parsedValue); } break; } - case "background-color": { + case BG_COLOR: { if (i !== l - 1) { return; } @@ -157,14 +165,14 @@ module.exports.parse = function parse(v, opt = {}) { } break; } - case "background-position": { + case BG_POSITION: { break; } - case "background-repeat": { + case BG_REPEAT: { bgRepeat.push(part); break; } - case "background-size": { + case BG_SIZE: { bgSize.push(part); break; } @@ -183,38 +191,38 @@ module.exports.parse = function parse(v, opt = {}) { } } if (bgPosition.length) { - const { parse: parser } = module.exports.shorthandFor.get("background-position"); + const { parse: parser } = module.exports.shorthandFor.get(BG_POSITION); const value = parser(bgPosition.join(" "), { globalObject, options }); if (value) { - bg["background-position"] = value; + bg[BG_POSITION] = value; } } if (bgSize.length) { - const { parse: parser } = module.exports.shorthandFor.get("background-size"); + const { parse: parser } = module.exports.shorthandFor.get(BG_SIZE); const value = parser(bgSize.join(" "), { globalObject, options }); if (value) { - bg["background-size"] = value; + bg[BG_SIZE] = value; } } if (bgRepeat.length) { - const { parse: parser } = module.exports.shorthandFor.get("background-repeat"); + const { parse: parser } = module.exports.shorthandFor.get(BG_REPEAT); const value = parser(bgRepeat.join(" "), { globalObject, options }); if (value) { - bg["background-repeat"] = value; + bg[BG_REPEAT] = value; } } if (bgBox.length) { switch (bgBox.length) { case 1: { const [value] = bgBox; - bg["background-origin"] = value; - bg["background-clip"] = value; + bg[BG_ORIGIN] = value; + bg[BG_CLIP] = value; break; } case 2: { const [value1, value2] = bgBox; - bg["background-origin"] = value1; - bg["background-clip"] = value2; + bg[BG_ORIGIN] = value1; + bg[BG_CLIP] = value2; break; } default: { @@ -231,14 +239,14 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (v === "" || parsers.hasVarFunc(v)) { for (const [key] of module.exports.shorthandFor) { this._setProperty(key, ""); } - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const bgValues = module.exports.parse(v, { globalObject: this._global, @@ -248,14 +256,14 @@ module.exports.definition = { return; } const bgMap = new Map([ - ["background-image", []], - ["background-position", []], - ["background-size", []], - ["background-repeat", []], - ["background-origin", []], - ["background-clip", []], - ["background-attachment", []], - ["background-color", []] + [BG_IMAGE, []], + [BG_POSITION, []], + [BG_SIZE, []], + [BG_REPEAT, []], + [BG_ORIGIN, []], + [BG_CLIP, []], + [BG_ATTACH, []], + [BG_COLOR, []] ]); const backgrounds = []; for (const bgValue of bgValues) { @@ -266,16 +274,16 @@ module.exports.definition = { arr.push(value); bgMap.set(longhand, arr); if (value !== module.exports.initialValues.get(longhand)) { - if (longhand === "background-size") { + if (longhand === BG_SIZE) { bg.push(`/ ${value}`); } else { bg.push(value); } - } else if (longhand === "background-image") { + } else if (longhand === BG_IMAGE) { if (v === "none") { bg.push(value); } - } else if (longhand === "background-color") { + } else if (longhand === BG_COLOR) { if (v === "transparent") { bg.push(value); } @@ -284,15 +292,15 @@ module.exports.definition = { } backgrounds.push(bg.join(" ")); } - const priority = this._priorities.get(property) ?? ""; + const priority = this._priorities.get(PROPERTY) ?? ""; for (const [longhand, value] of bgMap) { this._setProperty(longhand, value.join(", "), priority); } - this._setProperty(property, backgrounds.join(", "), priority); + this._setProperty(PROPERTY, backgrounds.join(", "), priority); } }, get() { - const v = this.getPropertyValue(property); + const v = this.getPropertyValue(PROPERTY); if (parsers.hasVarFunc(v)) { return v; } @@ -300,12 +308,8 @@ module.exports.definition = { let l = 0; for (const [longhand] of module.exports.shorthandFor) { const val = this.getPropertyValue(longhand); - if (longhand === "background-image") { - if ( - val === "none" && - v === "none" && - this.getPropertyValue("background-color") === "transparent" - ) { + if (longhand === BG_IMAGE) { + if (val === "none" && v === "none" && this.getPropertyValue(BG_COLOR) === "transparent") { return val; } if (val !== module.exports.initialValues.get(longhand)) { @@ -315,7 +319,7 @@ module.exports.definition = { l = imgValues.length; bgMap.set(longhand, imgValues); } - } else if (longhand === "background-color") { + } else if (longhand === BG_COLOR) { if (val !== module.exports.initialValues.get(longhand) || v.includes(val)) { bgMap.set(longhand, [val]); } @@ -334,7 +338,7 @@ module.exports.definition = { const bgValue = []; for (const [longhand, values] of bgMap) { switch (longhand) { - case "background-color": { + case BG_COLOR: { const [value] = values; if (parsers.hasVarFunc(value)) { return ""; @@ -342,7 +346,7 @@ module.exports.definition = { bgValue.push(value); break; } - case "background-size": { + case BG_SIZE: { const [value] = values; if (parsers.hasVarFunc(value)) { return ""; @@ -374,7 +378,7 @@ module.exports.definition = { for (const [longhand, values] of bgMap) { for (let i = 0; i < l; i++) { switch (longhand) { - case "background-color": { + case BG_COLOR: { if (i === l - 1) { const value = values[0]; if (parsers.hasVarFunc(value)) { @@ -387,7 +391,7 @@ module.exports.definition = { } break; } - case "background-size": { + case BG_SIZE: { const value = values[i]; if (parsers.hasVarFunc(value)) { return ""; diff --git a/lib/properties/backgroundAttachment.js b/lib/properties/backgroundAttachment.js index 82f3a034..43d318f7 100644 --- a/lib/properties/backgroundAttachment.js +++ b/lib/properties/backgroundAttachment.js @@ -1,19 +1,21 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-attachment"; -const shorthand = "background"; +const PROPERTY = "background-attachment"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -21,8 +23,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundClip.js b/lib/properties/backgroundClip.js index db220c3b..f96b0b3a 100644 --- a/lib/properties/backgroundClip.js +++ b/lib/properties/backgroundClip.js @@ -1,19 +1,21 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-clip"; -const shorthand = "background"; +const PROPERTY = "background-clip"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -21,8 +23,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundColor.js b/lib/properties/backgroundColor.js index fe6576da..fc1ca1be 100644 --- a/lib/properties/backgroundColor.js +++ b/lib/properties/backgroundColor.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-color"; -const shorthand = "background"; +const PROPERTY = "background-color"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -18,7 +20,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -34,27 +36,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundImage.js b/lib/properties/backgroundImage.js index 38c3d99c..072cbaf3 100644 --- a/lib/properties/backgroundImage.js +++ b/lib/properties/backgroundImage.js @@ -1,19 +1,21 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-image"; -const shorthand = "background"; +const PROPERTY = "background-image"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -21,12 +23,12 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } - case "Url": { + case AST_TYPES.URL: { const parsedValue = parsers.parseURL(value, options); if (!parsedValue) { return; @@ -57,27 +59,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundOrigin.js b/lib/properties/backgroundOrigin.js index 9eb96924..761461ff 100644 --- a/lib/properties/backgroundOrigin.js +++ b/lib/properties/backgroundOrigin.js @@ -1,19 +1,21 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-origin"; -const shorthand = "background"; +const PROPERTY = "background-origin"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -21,8 +23,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundPosition.js b/lib/properties/backgroundPosition.js index 5978becc..7c474e6c 100644 --- a/lib/properties/backgroundPosition.js +++ b/lib/properties/backgroundPosition.js @@ -1,37 +1,39 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-position"; -const shorthand = "background"; -const keyX = ["left", "right"]; -const keyY = ["top", "bottom"]; -const keywordsX = ["center", ...keyX]; -const keywordsY = ["center", ...keyY]; -const keywords = ["center", ...keyX, ...keyY]; +const PROPERTY = "background-position"; +const SHORTHAND = "background"; +const KEY_X = ["left", "right"]; +const KEY_Y = ["top", "bottom"]; +const KEYWORDS_X = ["center", ...KEY_X]; +const KEYWORDS_Y = ["center", ...KEY_Y]; +const KEYWORDS = ["center", ...KEY_X, ...KEY_Y]; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true }); if (Array.isArray(value) && value.length) { + const [part1, part2, part3, part4] = value; let parsedValue = ""; switch (value.length) { case 1: { - const [part1] = value; const val1 = - part1.type === "Identifier" + part1.type === AST_TYPES.IDENTIFIER ? part1.name : parsers.parseLengthPercentage([part1], options); if (val1) { @@ -46,43 +48,41 @@ module.exports.parse = function parse(v, opt = {}) { break; } case 2: { - const [part1, part2] = value; const val1 = - part1.type === "Identifier" + part1.type === AST_TYPES.IDENTIFIER ? part1.name : parsers.parseLengthPercentage([part1], options); const val2 = - part2.type === "Identifier" + part2.type === AST_TYPES.IDENTIFIER ? part2.name : parsers.parseLengthPercentage([part2], options); if (val1 && val2) { - if (keywordsX.includes(val1) && keywordsY.includes(val2)) { + if (KEYWORDS_X.includes(val1) && KEYWORDS_Y.includes(val2)) { parsedValue = `${val1} ${val2}`; - } else if (keywordsY.includes(val1) && keywordsX.includes(val2)) { + } else if (KEYWORDS_Y.includes(val1) && KEYWORDS_X.includes(val2)) { parsedValue = `${val2} ${val1}`; - } else if (keywordsX.includes(val1)) { - if (val2 === "center" || !keywordsX.includes(val2)) { + } else if (KEYWORDS_X.includes(val1)) { + if (val2 === "center" || !KEYWORDS_X.includes(val2)) { parsedValue = `${val1} ${val2}`; } - } else if (keywordsY.includes(val2)) { - if (!keywordsY.includes(val1)) { + } else if (KEYWORDS_Y.includes(val2)) { + if (!KEYWORDS_Y.includes(val1)) { parsedValue = `${val1} ${val2}`; } - } else if (!keywordsY.includes(val1) && !keywordsX.includes(val2)) { + } else if (!KEYWORDS_Y.includes(val1) && !KEYWORDS_X.includes(val2)) { parsedValue = `${val1} ${val2}`; } } break; } case 3: { - const [part1, part2, part3] = value; - const val1 = part1.type === "Identifier" && part1.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; const val2 = - part2.type === "Identifier" + part2.type === AST_TYPES.IDENTIFIER ? part2.name : parsers.parseLengthPercentage([part2], options); const val3 = - part3.type === "Identifier" + part3.type === AST_TYPES.IDENTIFIER ? part3.name : parsers.parseLengthPercentage([part3], options); if (val1 && val2 && val3) { @@ -90,29 +90,29 @@ module.exports.parse = function parse(v, opt = {}) { let offX = ""; let posY = ""; let offY = ""; - if (keywordsX.includes(val1)) { - if (keyY.includes(val2)) { - if (!keywords.includes(val3)) { + if (KEYWORDS_X.includes(val1)) { + if (KEY_Y.includes(val2)) { + if (!KEYWORDS.includes(val3)) { posX = val1; posY = val2; offY = val3; } - } else if (keyY.includes(val3)) { - if (!keywords.includes(val2)) { + } else if (KEY_Y.includes(val3)) { + if (!KEYWORDS.includes(val2)) { posX = val1; offX = val2; posY = val3; } } - } else if (keywordsY.includes(val1)) { - if (keyX.includes(val2)) { - if (!keywords.includes(val3)) { + } else if (KEYWORDS_Y.includes(val1)) { + if (KEY_X.includes(val2)) { + if (!KEYWORDS.includes(val3)) { posX = val2; offX = val3; posY = val1; } - } else if (keyX.includes(val3)) { - if (!keywords.includes(val2)) { + } else if (KEY_X.includes(val3)) { + if (!KEYWORDS.includes(val2)) { posX = val3; posY = val1; offY = val2; @@ -130,27 +130,26 @@ module.exports.parse = function parse(v, opt = {}) { break; } case 4: { - const [part1, part2, part3, part4] = value; - const val1 = part1.type === "Identifier" && part1.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; const val2 = parsers.parseLengthPercentage([part2], options); - const val3 = part3.type === "Identifier" && part3.name; + const val3 = part3.type === AST_TYPES.IDENTIFIER && part3.name; const val4 = parsers.parseLengthPercentage([part4], options); if (val1 && val2 && val3 && val4) { let posX = ""; let offX = ""; let posY = ""; let offY = ""; - if (keywordsX.includes(val1) && keyY.includes(val3)) { + if (KEYWORDS_X.includes(val1) && KEY_Y.includes(val3)) { posX = val1; offX = val2; posY = val3; offY = val4; - } else if (keyX.includes(val1) && keywordsY.includes(val3)) { + } else if (KEY_X.includes(val1) && KEYWORDS_Y.includes(val3)) { posX = val1; offX = val2; posY = val3; offY = val4; - } else if (keyY.includes(val1) && keywordsX.includes(val3)) { + } else if (KEY_Y.includes(val1) && KEYWORDS_X.includes(val3)) { posX = val3; offX = val4; posY = val1; @@ -182,27 +181,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundRepeat.js b/lib/properties/backgroundRepeat.js index 1f742b06..161b4ac4 100644 --- a/lib/properties/backgroundRepeat.js +++ b/lib/properties/backgroundRepeat.js @@ -1,21 +1,23 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-repeat"; -const shorthand = "background"; +const PROPERTY = "background-repeat"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -25,7 +27,7 @@ module.exports.parse = function parse(v, opt = {}) { switch (value.length) { case 1: { const [part1] = value; - const val1 = part1.type === "Identifier" && part1.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; if (val1) { parsedValue = val1; } @@ -33,8 +35,8 @@ module.exports.parse = function parse(v, opt = {}) { } case 2: { const [part1, part2] = value; - const val1 = part1.type === "Identifier" && part1.name; - const val2 = part2.type === "Identifier" && part2.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; + const val2 = part2.type === AST_TYPES.IDENTIFIER && part2.name; if (val1 && val2) { if (val1 === "repeat" && val2 === "no-repeat") { parsedValue = "repeat-x"; @@ -68,27 +70,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/backgroundSize.js b/lib/properties/backgroundSize.js index b720e938..9c009154 100644 --- a/lib/properties/backgroundSize.js +++ b/lib/properties/backgroundSize.js @@ -1,21 +1,23 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "background-size"; -const shorthand = "background"; +const PROPERTY = "background-size"; +const SHORTHAND = "background"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -24,15 +26,15 @@ module.exports.parse = function parse(v, opt = {}) { if (value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } parsedValues.push(`${name}(${itemValue})`); break; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -47,36 +49,36 @@ module.exports.parse = function parse(v, opt = {}) { } else { const [val1, val2] = value; const parts = []; - if (val1.type === "Calc" && !val1.isNumber) { + if (val1.type === AST_TYPES.CALC && !val1.isNumber) { parts.push(`${val1.name}(${val1.value})`); - } else if (val1.type === "Identifier") { + } else if (val1.type === AST_TYPES.IDENTIFIER) { parts.push(val1.name); - } else if (val1.type === "Dimension") { + } else if (val1.type === AST_TYPES.DIMENSION) { parts.push(`${val1.value}${val1.unit}`); - } else if (val1.type === "Percentage") { + } else if (val1.type === AST_TYPES.PERCENTAGE) { parts.push(`${val1.value}%`); } else { return; } switch (val2.type) { - case "Calc": { + case AST_TYPES.CALC: { if (val2.isNumber) { return; } parts.push(`${val2.name}(${val2.value})`); break; } - case "Dimension": { + case AST_TYPES.DIMENSION: { parts.push(`${val2.value}${val2.unit}`); break; } - case "Identifier": { + case AST_TYPES.IDENTIFIER: { if (val2.name !== "auto") { parts.push(val2.name); } break; } - case "Percentage": { + case AST_TYPES.PERCENTAGE: { parts.push(`${val2.value}%`); break; } @@ -99,27 +101,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/border.js b/lib/properties/border.js index 289b0377..3f70ccbc 100644 --- a/lib/properties/border.js +++ b/lib/properties/border.js @@ -1,6 +1,7 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderWidth = require("./borderWidth"); const borderStyle = require("./borderStyle"); const borderColor = require("./borderColor"); @@ -9,18 +10,21 @@ const borderRight = require("./borderRight"); const borderBottom = require("./borderBottom"); const borderLeft = require("./borderLeft"); -const property = "border"; +const PROPERTY = "border"; +const BORDER_WIDTH = "border-width"; +const BORDER_STYLE = "border-style"; +const BORDER_COLOR = "border-color"; module.exports.initialValues = new Map([ - ["border-width", "medium"], - ["border-style", "none"], - ["border-color", "currentcolor"] + [BORDER_WIDTH, "medium"], + [BORDER_STYLE, "none"], + [BORDER_COLOR, "currentcolor"] ]); module.exports.shorthandFor = new Map([ - ["border-width", borderWidth], - ["border-style", borderStyle], - ["border-color", borderColor] + [BORDER_WIDTH, borderWidth], + [BORDER_STYLE, borderStyle], + [BORDER_COLOR, borderColor] ]); module.exports.positionShorthandFor = new Map([ @@ -34,11 +38,12 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "" || parsers.hasVarFunc(v)) { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -46,16 +51,16 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { - if (isNumber || parsedValues.has("border-width")) { + case AST_TYPES.CALC: { + if (isNumber || parsedValues.has(BORDER_WIDTH)) { return; } - parsedValues.set("border-width", `${name}(${itemValue})`); + parsedValues.set(BORDER_WIDTH, `${name}(${itemValue})`); break; } - case "Dimension": - case "Number": { - if (parsedValues.has("border-width")) { + case AST_TYPES.DIMENSION: + case AST_TYPES.NUMBER: { + if (parsedValues.has(BORDER_WIDTH)) { return; } const parseOpt = { @@ -68,52 +73,52 @@ module.exports.parse = function parse(v, opt = {}) { if (!parsedValue) { return; } - parsedValues.set("border-width", parsedValue); + parsedValues.set(BORDER_WIDTH, parsedValue); break; } - case "Function": { - if (parsedValues.has("border-color")) { + case AST_TYPES.FUNCTION: { + if (parsedValues.has(BORDER_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-color", parsedValue); + parsedValues.set(BORDER_COLOR, parsedValue); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Hash": { - if (parsedValues.has("border-color")) { + case AST_TYPES.HASH: { + if (parsedValues.has(BORDER_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-color", parsedValue); + parsedValues.set(BORDER_COLOR, parsedValue); break; } - case "Identifier": { - if (parsers.isValidPropertyValue("border-width", name, globalObject)) { - if (parsedValues.has("border-width")) { + case AST_TYPES.IDENTIFIER: { + if (parsers.isValidPropertyValue(BORDER_WIDTH, name, globalObject)) { + if (parsedValues.has(BORDER_WIDTH)) { return; } - parsedValues.set("border-width", name); + parsedValues.set(BORDER_WIDTH, name); break; - } else if (parsers.isValidPropertyValue("border-style", name, globalObject)) { - if (parsedValues.has("border-style")) { + } else if (parsers.isValidPropertyValue(BORDER_STYLE, name, globalObject)) { + if (parsedValues.has(BORDER_STYLE)) { return; } - parsedValues.set("border-style", name); + parsedValues.set(BORDER_STYLE, name); break; - } else if (parsers.isValidPropertyValue("border-color", name, globalObject)) { - if (parsedValues.has("border-color")) { + } else if (parsers.isValidPropertyValue(BORDER_COLOR, name, globalObject)) { + if (parsedValues.has(BORDER_COLOR)) { return; } - parsedValues.set("border-color", name); + parsedValues.set(BORDER_COLOR, name); break; } return; @@ -129,15 +134,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.size) { const keys = module.exports.shorthandFor.keys(); const obj = { - "border-width": "medium" + [BORDER_WIDTH]: "medium" }; for (const key of keys) { if (parsedValues.has(key)) { const parsedValue = parsedValues.get(key); if (parsedValue !== module.exports.initialValues.get(key)) { obj[key] = parsedValues.get(key); - if (obj["border-width"] && obj["border-width"] === "medium") { - delete obj["border-width"]; + if (obj[BORDER_WIDTH] && obj[BORDER_WIDTH] === "medium") { + delete obj[BORDER_WIDTH]; } } } @@ -150,24 +155,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (val || typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._borderSetter(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderBottom.js b/lib/properties/borderBottom.js index 943c4cc9..c1596ef9 100644 --- a/lib/properties/borderBottom.js +++ b/lib/properties/borderBottom.js @@ -1,34 +1,39 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderBottomWidth = require("./borderBottomWidth"); const borderBottomStyle = require("./borderBottomStyle"); const borderBottomColor = require("./borderBottomColor"); -const property = "border-bottom"; -const shorthand = "border"; +const PROPERTY = "border-bottom"; +const SHORTHAND = "border"; +const BORDER_BOTTOM_WIDTH = "border-bottom-width"; +const BORDER_BOTTOM_STYLE = "border-bottom-style"; +const BORDER_BOTTOM_COLOR = "border-bottom-color"; module.exports.initialValues = new Map([ - ["border-bottom-width", "medium"], - ["border-bottom-style", "none"], - ["border-bottom-color", "currentcolor"] + [BORDER_BOTTOM_WIDTH, "medium"], + [BORDER_BOTTOM_STYLE, "none"], + [BORDER_BOTTOM_COLOR, "currentcolor"] ]); module.exports.shorthandFor = new Map([ - ["border-bottom-width", borderBottomWidth], - ["border-bottom-style", borderBottomStyle], - ["border-bottom-color", borderBottomColor] + [BORDER_BOTTOM_WIDTH, borderBottomWidth], + [BORDER_BOTTOM_STYLE, borderBottomStyle], + [BORDER_BOTTOM_COLOR, borderBottomColor] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -36,16 +41,16 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { - if (isNumber || parsedValues.has("border-bottom-width")) { + case AST_TYPES.CALC: { + if (isNumber || parsedValues.has(BORDER_BOTTOM_WIDTH)) { return; } - parsedValues.set("border-bottom-width", `${name}(${itemValue})`); + parsedValues.set(BORDER_BOTTOM_WIDTH, `${name}(${itemValue})`); break; } - case "Dimension": - case "Number": { - if (parsedValues.has("border-bottom-width")) { + case AST_TYPES.DIMENSION: + case AST_TYPES.NUMBER: { + if (parsedValues.has(BORDER_BOTTOM_WIDTH)) { return; } const parseOpt = { @@ -58,52 +63,52 @@ module.exports.parse = function parse(v, opt = {}) { if (!parsedValue) { return; } - parsedValues.set("border-bottom-width", parsedValue); + parsedValues.set(BORDER_BOTTOM_WIDTH, parsedValue); break; } - case "Function": { - if (parsedValues.has("border-bottom-color")) { + case AST_TYPES.FUNCTION: { + if (parsedValues.has(BORDER_BOTTOM_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-bottom-color", parsedValue); + parsedValues.set(BORDER_BOTTOM_COLOR, parsedValue); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Hash": { - if (parsedValues.has("border-bottom-color")) { + case AST_TYPES.HASH: { + if (parsedValues.has(BORDER_BOTTOM_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-bottom-color", parsedValue); + parsedValues.set(BORDER_BOTTOM_COLOR, parsedValue); break; } - case "Identifier": { - if (parsers.isValidPropertyValue("border-bottom-width", name, globalObject)) { - if (parsedValues.has("border-bottom-width")) { + case AST_TYPES.IDENTIFIER: { + if (parsers.isValidPropertyValue(BORDER_BOTTOM_WIDTH, name, globalObject)) { + if (parsedValues.has(BORDER_BOTTOM_WIDTH)) { return; } - parsedValues.set("border-bottom-width", name); + parsedValues.set(BORDER_BOTTOM_WIDTH, name); break; - } else if (parsers.isValidPropertyValue("border-bottom-style", name, globalObject)) { - if (parsedValues.has("border-bottom-style")) { + } else if (parsers.isValidPropertyValue(BORDER_BOTTOM_STYLE, name, globalObject)) { + if (parsedValues.has(BORDER_BOTTOM_STYLE)) { return; } - parsedValues.set("border-bottom-style", name); + parsedValues.set(BORDER_BOTTOM_STYLE, name); break; - } else if (parsers.isValidPropertyValue("border-bottom-color", name, globalObject)) { - if (parsedValues.has("border-bottom-color")) { + } else if (parsers.isValidPropertyValue(BORDER_BOTTOM_COLOR, name, globalObject)) { + if (parsedValues.has(BORDER_BOTTOM_COLOR)) { return; } - parsedValues.set("border-bottom-color", name); + parsedValues.set(BORDER_BOTTOM_COLOR, name); break; } return; @@ -119,15 +124,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.size) { const keys = module.exports.shorthandFor.keys(); const obj = { - "border-bottom-width": "medium" + [BORDER_BOTTOM_WIDTH]: "medium" }; for (const key of keys) { if (parsedValues.has(key)) { const parsedValue = parsedValues.get(key); if (parsedValue !== module.exports.initialValues.get(key)) { obj[key] = parsedValues.get(key); - if (obj["border-bottom-width"] && obj["border-bottom-width"] === "medium") { - delete obj["border-bottom-width"]; + if (obj[BORDER_BOTTOM_WIDTH] && obj[BORDER_BOTTOM_WIDTH] === "medium") { + delete obj[BORDER_BOTTOM_WIDTH]; } } } @@ -140,26 +145,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (val || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderBottomColor.js b/lib/properties/borderBottomColor.js index 58fd03cb..102fd359 100644 --- a/lib/properties/borderBottomColor.js +++ b/lib/properties/borderBottomColor.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-bottom-color"; -const lineShorthand = "border-color"; -const positionShorthand = "border-bottom"; -const shorthand = "border"; +const PROPERTY = "border-bottom-color"; +const LINE_SHORTHAND = "border-color"; +const POSITION_SHORTHAND = "border-bottom"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,7 +22,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -36,30 +38,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderBottomStyle.js b/lib/properties/borderBottomStyle.js index 4b9cd8d8..27ef9e98 100644 --- a/lib/properties/borderBottomStyle.js +++ b/lib/properties/borderBottomStyle.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-bottom-style"; -const lineShorthand = "border-style"; -const positionShorthand = "border-bottom"; -const shorthand = "border"; +const PROPERTY = "border-bottom-style"; +const LINE_SHORTHAND = "border-style"; +const POSITION_SHORTHAND = "border-bottom"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,8 +22,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -35,30 +37,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderBottomWidth.js b/lib/properties/borderBottomWidth.js index 08c48d5c..d291d6e7 100644 --- a/lib/properties/borderBottomWidth.js +++ b/lib/properties/borderBottomWidth.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-bottom-width"; -const lineShorthand = "border-width"; -const positionShorthand = "border-bottom"; -const shorthand = "border"; +const PROPERTY = "border-bottom-width"; +const LINE_SHORTHAND = "border-width"; +const POSITION_SHORTHAND = "border-bottom"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -49,30 +51,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderCollapse.js b/lib/properties/borderCollapse.js index 0c37fd50..60d133ea 100644 --- a/lib/properties/borderCollapse.js +++ b/lib/properties/borderCollapse.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-collapse"; +const PROPERTY = "border-collapse"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,8 +19,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -32,24 +34,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderColor.js b/lib/properties/borderColor.js index 417e2370..c8da4344 100644 --- a/lib/properties/borderColor.js +++ b/lib/properties/borderColor.js @@ -1,27 +1,33 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderTopColor = require("./borderTopColor"); const borderRightColor = require("./borderRightColor"); const borderBottomColor = require("./borderBottomColor"); const borderLeftColor = require("./borderLeftColor"); -const property = "border-color"; -const shorthand = "border"; +const PROPERTY = "border-color"; +const SHORTHAND = "border"; +const BORDER_TOP_COLOR = "border-top-color"; +const BORDER_RIGHT_COLOR = "border-right-color"; +const BORDER_BOTTOM_COLOR = "border-bottom-color"; +const BORDER_LEFT_COLOR = "border-left-color"; module.exports.shorthandFor = new Map([ - ["border-top-color", borderTopColor], - ["border-right-color", borderRightColor], - ["border-bottom-color", borderBottomColor], - ["border-left-color", borderLeftColor] + [BORDER_TOP_COLOR, borderTopColor], + [BORDER_RIGHT_COLOR, borderRightColor], + [BORDER_BOTTOM_COLOR, borderBottomColor], + [BORDER_LEFT_COLOR, borderLeftColor] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const values = parsers.parsePropertyValue(property, v, { + const values = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -34,7 +40,7 @@ module.exports.parse = function parse(v, opt = {}) { for (const value of values) { const { name, type } = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { if (values.length !== 1) { return; } @@ -52,20 +58,20 @@ module.exports.parse = function parse(v, opt = {}) { } else if (typeof values === "string") { parsedValues.push(values); } - if (parsedValues.length) { - switch (parsedValues.length) { + const l = parsedValues.length; + if (l) { + const [val1, val2, val3, val4] = parsedValues; + switch (l) { case 1: { return parsedValues; } case 2: { - const [val1, val2] = parsedValues; if (val1 === val2) { return [val1]; } return parsedValues; } case 3: { - const [val1, val2, val3] = parsedValues; if (val1 === val3) { if (val1 === val2) { return [val1]; @@ -75,7 +81,6 @@ module.exports.parse = function parse(v, opt = {}) { return parsedValues; } case 4: { - const [val1, val2, val3, val4] = parsedValues; if (val2 === val4) { if (val1 === val3) { if (val1 === val2) { @@ -96,26 +101,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (Array.isArray(val) || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderLeft.js b/lib/properties/borderLeft.js index 1ff7a7e0..8b03f357 100644 --- a/lib/properties/borderLeft.js +++ b/lib/properties/borderLeft.js @@ -1,34 +1,39 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderLeftWidth = require("./borderLeftWidth"); const borderLeftStyle = require("./borderLeftStyle"); const borderLeftColor = require("./borderLeftColor"); -const property = "border-left"; -const shorthand = "border"; +const PROPERTY = "border-left"; +const SHORTHAND = "border"; +const BORDER_LEFT_WIDTH = "border-left-width"; +const BORDER_LEFT_STYLE = "border-left-style"; +const BORDER_LEFT_COLOR = "border-left-color"; module.exports.initialValues = new Map([ - ["border-left-width", "medium"], - ["border-left-style", "none"], - ["border-left-color", "currentcolor"] + [BORDER_LEFT_WIDTH, "medium"], + [BORDER_LEFT_STYLE, "none"], + [BORDER_LEFT_COLOR, "currentcolor"] ]); module.exports.shorthandFor = new Map([ - ["border-left-width", borderLeftWidth], - ["border-left-style", borderLeftStyle], - ["border-left-color", borderLeftColor] + [BORDER_LEFT_WIDTH, borderLeftWidth], + [BORDER_LEFT_STYLE, borderLeftStyle], + [BORDER_LEFT_COLOR, borderLeftColor] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -36,16 +41,16 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { - if (isNumber || parsedValues.has("border-left-width")) { + case AST_TYPES.CALC: { + if (isNumber || parsedValues.has(BORDER_LEFT_WIDTH)) { return; } - parsedValues.set("border-left-width", `${name}(${itemValue})`); + parsedValues.set(BORDER_LEFT_WIDTH, `${name}(${itemValue})`); break; } - case "Dimension": - case "Number": { - if (parsedValues.has("border-left-width")) { + case AST_TYPES.DIMENSION: + case AST_TYPES.NUMBER: { + if (parsedValues.has(BORDER_LEFT_WIDTH)) { return; } const parseOpt = { @@ -58,52 +63,52 @@ module.exports.parse = function parse(v, opt = {}) { if (!parsedValue) { return; } - parsedValues.set("border-left-width", parsedValue); + parsedValues.set(BORDER_LEFT_WIDTH, parsedValue); break; } - case "Function": { - if (parsedValues.has("border-left-color")) { + case AST_TYPES.FUNCTION: { + if (parsedValues.has(BORDER_LEFT_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-left-color", parsedValue); + parsedValues.set(BORDER_LEFT_COLOR, parsedValue); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Hash": { - if (parsedValues.has("border-left-color")) { + case AST_TYPES.HASH: { + if (parsedValues.has(BORDER_LEFT_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-left-color", parsedValue); + parsedValues.set(BORDER_LEFT_COLOR, parsedValue); break; } - case "Identifier": { - if (parsers.isValidPropertyValue("border-left-width", name, globalObject)) { - if (parsedValues.has("border-left-width")) { + case AST_TYPES.IDENTIFIER: { + if (parsers.isValidPropertyValue(BORDER_LEFT_WIDTH, name, globalObject)) { + if (parsedValues.has(BORDER_LEFT_WIDTH)) { return; } - parsedValues.set("border-left-width", name); + parsedValues.set(BORDER_LEFT_WIDTH, name); break; - } else if (parsers.isValidPropertyValue("border-left-style", name, globalObject)) { - if (parsedValues.has("border-left-style")) { + } else if (parsers.isValidPropertyValue(BORDER_LEFT_STYLE, name, globalObject)) { + if (parsedValues.has(BORDER_LEFT_STYLE)) { return; } - parsedValues.set("border-left-style", name); + parsedValues.set(BORDER_LEFT_STYLE, name); break; - } else if (parsers.isValidPropertyValue("border-left-color", name, globalObject)) { - if (parsedValues.has("border-left-color")) { + } else if (parsers.isValidPropertyValue(BORDER_LEFT_COLOR, name, globalObject)) { + if (parsedValues.has(BORDER_LEFT_COLOR)) { return; } - parsedValues.set("border-left-color", name); + parsedValues.set(BORDER_LEFT_COLOR, name); break; } return; @@ -119,15 +124,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.size) { const keys = module.exports.shorthandFor.keys(); const obj = { - "border-left-width": "medium" + [BORDER_LEFT_WIDTH]: "medium" }; for (const key of keys) { if (parsedValues.has(key)) { const parsedValue = parsedValues.get(key); if (parsedValue !== module.exports.initialValues.get(key)) { obj[key] = parsedValues.get(key); - if (obj["border-left-width"] && obj["border-left-width"] === "medium") { - delete obj["border-left-width"]; + if (obj[BORDER_LEFT_WIDTH] && obj[BORDER_LEFT_WIDTH] === "medium") { + delete obj[BORDER_LEFT_WIDTH]; } } } @@ -140,26 +145,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (val || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderLeftColor.js b/lib/properties/borderLeftColor.js index 35cc430d..9fc5b584 100644 --- a/lib/properties/borderLeftColor.js +++ b/lib/properties/borderLeftColor.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-left-color"; -const lineShorthand = "border-color"; -const positionShorthand = "border-left"; -const shorthand = "border"; +const PROPERTY = "border-left-color"; +const LINE_SHORTHAND = "border-color"; +const POSITION_SHORTHAND = "border-left"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,7 +22,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -36,30 +38,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderLeftStyle.js b/lib/properties/borderLeftStyle.js index 1ed8bcfa..e3dde51c 100644 --- a/lib/properties/borderLeftStyle.js +++ b/lib/properties/borderLeftStyle.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-left-style"; -const lineShorthand = "border-style"; -const positionShorthand = "border-left"; -const shorthand = "border"; +const PROPERTY = "border-left-style"; +const LINE_SHORTHAND = "border-style"; +const POSITION_SHORTHAND = "border-left"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,8 +22,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -35,30 +37,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderLeftWidth.js b/lib/properties/borderLeftWidth.js index 6fe9a74d..ac2b4a21 100644 --- a/lib/properties/borderLeftWidth.js +++ b/lib/properties/borderLeftWidth.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-left-width"; -const lineShorthand = "border-width"; -const positionShorthand = "border-left"; -const shorthand = "border"; +const PROPERTY = "border-left-width"; +const LINE_SHORTHAND = "border-width"; +const POSITION_SHORTHAND = "border-left"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -49,30 +51,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderRight.js b/lib/properties/borderRight.js index 29268f14..2f52b448 100644 --- a/lib/properties/borderRight.js +++ b/lib/properties/borderRight.js @@ -1,34 +1,39 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderRightWidth = require("./borderRightWidth"); const borderRightStyle = require("./borderRightStyle"); const borderRightColor = require("./borderRightColor"); -const property = "border-right"; -const shorthand = "border"; +const PROPERTY = "border-right"; +const SHORTHAND = "border"; +const BORDER_RIGHT_WIDTH = "border-right-width"; +const BORDER_RIGHT_STYLE = "border-right-style"; +const BORDER_RIGHT_COLOR = "border-right-color"; module.exports.initialValues = new Map([ - ["border-right-width", "medium"], - ["border-right-style", "none"], - ["border-right-color", "currentcolor"] + [BORDER_RIGHT_WIDTH, "medium"], + [BORDER_RIGHT_STYLE, "none"], + [BORDER_RIGHT_COLOR, "currentcolor"] ]); module.exports.shorthandFor = new Map([ - ["border-right-width", borderRightWidth], - ["border-right-style", borderRightStyle], - ["border-right-color", borderRightColor] + [BORDER_RIGHT_WIDTH, borderRightWidth], + [BORDER_RIGHT_STYLE, borderRightStyle], + [BORDER_RIGHT_COLOR, borderRightColor] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -36,16 +41,16 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { - if (isNumber || parsedValues.has("border-right-width")) { + case AST_TYPES.CALC: { + if (isNumber || parsedValues.has(BORDER_RIGHT_WIDTH)) { return; } - parsedValues.set("border-right-width", `${name}(${itemValue})`); + parsedValues.set(BORDER_RIGHT_WIDTH, `${name}(${itemValue})`); break; } - case "Dimension": - case "Number": { - if (parsedValues.has("border-right-width")) { + case AST_TYPES.DIMENSION: + case AST_TYPES.NUMBER: { + if (parsedValues.has(BORDER_RIGHT_WIDTH)) { return; } const parseOpt = { @@ -58,52 +63,52 @@ module.exports.parse = function parse(v, opt = {}) { if (!parsedValue) { return; } - parsedValues.set("border-right-width", parsedValue); + parsedValues.set(BORDER_RIGHT_WIDTH, parsedValue); break; } - case "Function": { - if (parsedValues.has("border-right-color")) { + case AST_TYPES.FUNCTION: { + if (parsedValues.has(BORDER_RIGHT_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-right-color", parsedValue); + parsedValues.set(BORDER_RIGHT_COLOR, parsedValue); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Hash": { - if (parsedValues.has("border-right-color")) { + case AST_TYPES.HASH: { + if (parsedValues.has(BORDER_RIGHT_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-right-color", parsedValue); + parsedValues.set(BORDER_RIGHT_COLOR, parsedValue); break; } - case "Identifier": { - if (parsers.isValidPropertyValue("border-right-width", name, globalObject)) { - if (parsedValues.has("border-right-width")) { + case AST_TYPES.IDENTIFIER: { + if (parsers.isValidPropertyValue(BORDER_RIGHT_WIDTH, name, globalObject)) { + if (parsedValues.has(BORDER_RIGHT_WIDTH)) { return; } - parsedValues.set("border-right-width", name); + parsedValues.set(BORDER_RIGHT_WIDTH, name); break; - } else if (parsers.isValidPropertyValue("border-right-style", name, globalObject)) { - if (parsedValues.has("border-right-style")) { + } else if (parsers.isValidPropertyValue(BORDER_RIGHT_STYLE, name, globalObject)) { + if (parsedValues.has(BORDER_RIGHT_STYLE)) { return; } - parsedValues.set("border-right-style", name); + parsedValues.set(BORDER_RIGHT_STYLE, name); break; - } else if (parsers.isValidPropertyValue("border-right-color", name, globalObject)) { - if (parsedValues.has("border-right-color")) { + } else if (parsers.isValidPropertyValue(BORDER_RIGHT_COLOR, name, globalObject)) { + if (parsedValues.has(BORDER_RIGHT_COLOR)) { return; } - parsedValues.set("border-right-color", name); + parsedValues.set(BORDER_RIGHT_COLOR, name); break; } return; @@ -119,15 +124,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.size) { const keys = module.exports.shorthandFor.keys(); const obj = { - "border-right-width": "medium" + [BORDER_RIGHT_WIDTH]: "medium" }; for (const key of keys) { if (parsedValues.has(key)) { const parsedValue = parsedValues.get(key); if (parsedValue !== module.exports.initialValues.get(key)) { obj[key] = parsedValues.get(key); - if (obj["border-right-width"] && obj["border-right-width"] === "medium") { - delete obj["border-right-width"]; + if (obj[BORDER_RIGHT_WIDTH] && obj[BORDER_RIGHT_WIDTH] === "medium") { + delete obj[BORDER_RIGHT_WIDTH]; } } } @@ -140,26 +145,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (val || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderRightColor.js b/lib/properties/borderRightColor.js index 2adb3fc8..60c44166 100644 --- a/lib/properties/borderRightColor.js +++ b/lib/properties/borderRightColor.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-right-color"; -const lineShorthand = "border-color"; -const positionShorthand = "border-right"; -const shorthand = "border"; +const PROPERTY = "border-right-color"; +const LINE_SHORTHAND = "border-color"; +const POSITION_SHORTHAND = "border-right"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,7 +22,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -36,30 +38,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderRightStyle.js b/lib/properties/borderRightStyle.js index 33ed47f7..71336b81 100644 --- a/lib/properties/borderRightStyle.js +++ b/lib/properties/borderRightStyle.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-right-style"; -const lineShorthand = "border-style"; -const positionShorthand = "border-right"; -const shorthand = "border"; +const PROPERTY = "border-right-style"; +const LINE_SHORTHAND = "border-style"; +const POSITION_SHORTHAND = "border-right"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,8 +22,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -35,30 +37,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderRightWidth.js b/lib/properties/borderRightWidth.js index 34893b4f..13aa1900 100644 --- a/lib/properties/borderRightWidth.js +++ b/lib/properties/borderRightWidth.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-right-width"; -const lineShorthand = "border-width"; -const positionShorthand = "border-right"; -const shorthand = "border"; +const PROPERTY = "border-right-width"; +const LINE_SHORTHAND = "border-width"; +const POSITION_SHORTHAND = "border-right"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -49,30 +51,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderSpacing.js b/lib/properties/borderSpacing.js index 438bc06d..e8bae47c 100644 --- a/lib/properties/borderSpacing.js +++ b/lib/properties/borderSpacing.js @@ -1,23 +1,30 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-spacing"; +const PROPERTY = "border-spacing"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true }); - if (Array.isArray(value) && value.length) { - switch (value.length) { + if (Array.isArray(value)) { + const l = value.length; + const [part1, part2] = value; + switch (l) { case 1: { - const [part1] = value; + const { name, type } = part1; + if (type === AST_TYPES.GLOBAL_KEY) { + return name; + } const val1 = parsers.parseLength([part1], options); if (val1) { return val1; @@ -25,7 +32,6 @@ module.exports.parse = function parse(v, opt = {}) { break; } case 2: { - const [part1, part2] = value; const val1 = parsers.parseLength([part1], options); const val2 = parsers.parseLength([part2], options); if (val1 && val2) { @@ -44,24 +50,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderStyle.js b/lib/properties/borderStyle.js index 59fd3ace..787a0322 100644 --- a/lib/properties/borderStyle.js +++ b/lib/properties/borderStyle.js @@ -1,27 +1,33 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderTopStyle = require("./borderTopStyle"); const borderRightStyle = require("./borderRightStyle"); const borderBottomStyle = require("./borderBottomStyle"); const borderLeftStyle = require("./borderLeftStyle"); -const property = "border-style"; -const shorthand = "border"; +const PROPERTY = "border-style"; +const SHORTHAND = "border"; +const BORDER_TOP_STYLE = "border-top-style"; +const BORDER_RIGHT_STYLE = "border-right-style"; +const BORDER_BOTTOM_STYLE = "border-bottom-style"; +const BORDER_LEFT_STYLE = "border-left-style"; module.exports.shorthandFor = new Map([ - ["border-top-style", borderTopStyle], - ["border-right-style", borderRightStyle], - ["border-bottom-style", borderBottomStyle], - ["border-left-style", borderLeftStyle] + [BORDER_TOP_STYLE, borderTopStyle], + [BORDER_RIGHT_STYLE, borderRightStyle], + [BORDER_BOTTOM_STYLE, borderBottomStyle], + [BORDER_LEFT_STYLE, borderLeftStyle] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const values = parsers.parsePropertyValue(property, v, { + const values = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -34,13 +40,13 @@ module.exports.parse = function parse(v, opt = {}) { for (const value of values) { const { name, type } = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { if (values.length !== 1) { return; } return name; } - case "Identifier": { + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -52,20 +58,20 @@ module.exports.parse = function parse(v, opt = {}) { } else if (typeof values === "string") { parsedValues.push(values); } - if (parsedValues.length) { - switch (parsedValues.length) { + const l = parsedValues.length; + if (l) { + const [val1, val2, val3, val4] = parsedValues; + switch (l) { case 1: { return parsedValues; } case 2: { - const [val1, val2] = parsedValues; if (val1 === val2) { return [val1]; } return parsedValues; } case 3: { - const [val1, val2, val3] = parsedValues; if (val1 === val3) { if (val1 === val2) { return [val1]; @@ -75,7 +81,6 @@ module.exports.parse = function parse(v, opt = {}) { return parsedValues; } case 4: { - const [val1, val2, val3, val4] = parsedValues; if (val2 === val4) { if (val1 === val3) { if (val1 === val2) { @@ -96,26 +101,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (Array.isArray(val) || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderTop.js b/lib/properties/borderTop.js index 5883a653..0c2851d6 100644 --- a/lib/properties/borderTop.js +++ b/lib/properties/borderTop.js @@ -1,34 +1,39 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderTopWidth = require("./borderTopWidth"); const borderTopStyle = require("./borderTopStyle"); const borderTopColor = require("./borderTopColor"); -const property = "border-top"; -const shorthand = "border"; +const PROPERTY = "border-top"; +const SHORTHAND = "border"; +const BORDER_TOP_WIDTH = "border-top-width"; +const BORDER_TOP_STYLE = "border-top-style"; +const BORDER_TOP_COLOR = "border-top-color"; module.exports.initialValues = new Map([ - ["border-top-width", "medium"], - ["border-top-style", "none"], - ["border-top-color", "currentcolor"] + [BORDER_TOP_WIDTH, "medium"], + [BORDER_TOP_STYLE, "none"], + [BORDER_TOP_COLOR, "currentcolor"] ]); module.exports.shorthandFor = new Map([ - ["border-top-width", borderTopWidth], - ["border-top-style", borderTopStyle], - ["border-top-color", borderTopColor] + [BORDER_TOP_WIDTH, borderTopWidth], + [BORDER_TOP_STYLE, borderTopStyle], + [BORDER_TOP_COLOR, borderTopColor] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = new Map(); for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -36,16 +41,16 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { - if (isNumber || parsedValues.has("border-top-width")) { + case AST_TYPES.CALC: { + if (isNumber || parsedValues.has(BORDER_TOP_WIDTH)) { return; } - parsedValues.set("border-top-width", `${name}(${itemValue})`); + parsedValues.set(BORDER_TOP_WIDTH, `${name}(${itemValue})`); break; } - case "Dimension": - case "Number": { - if (parsedValues.has("border-top-width")) { + case AST_TYPES.DIMENSION: + case AST_TYPES.NUMBER: { + if (parsedValues.has(BORDER_TOP_WIDTH)) { return; } const parseOpt = { @@ -58,52 +63,52 @@ module.exports.parse = function parse(v, opt = {}) { if (!parsedValue) { return; } - parsedValues.set("border-top-width", parsedValue); + parsedValues.set(BORDER_TOP_WIDTH, parsedValue); break; } - case "Function": { - if (parsedValues.has("border-top-color")) { + case AST_TYPES.FUNCTION: { + if (parsedValues.has(BORDER_TOP_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-top-color", parsedValue); + parsedValues.set(BORDER_TOP_COLOR, parsedValue); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Hash": { - if (parsedValues.has("border-top-color")) { + case AST_TYPES.HASH: { + if (parsedValues.has(BORDER_TOP_COLOR)) { return; } const parsedValue = parsers.parseColor(value, options); if (!parsedValue) { return; } - parsedValues.set("border-top-color", parsedValue); + parsedValues.set(BORDER_TOP_COLOR, parsedValue); break; } - case "Identifier": { - if (parsers.isValidPropertyValue("border-top-width", name, globalObject)) { - if (parsedValues.has("border-top-width")) { + case AST_TYPES.IDENTIFIER: { + if (parsers.isValidPropertyValue(BORDER_TOP_WIDTH, name, globalObject)) { + if (parsedValues.has(BORDER_TOP_WIDTH)) { return; } - parsedValues.set("border-top-width", name); + parsedValues.set(BORDER_TOP_WIDTH, name); break; - } else if (parsers.isValidPropertyValue("border-top-style", name, globalObject)) { - if (parsedValues.has("border-top-style")) { + } else if (parsers.isValidPropertyValue(BORDER_TOP_STYLE, name, globalObject)) { + if (parsedValues.has(BORDER_TOP_STYLE)) { return; } - parsedValues.set("border-top-style", name); + parsedValues.set(BORDER_TOP_STYLE, name); break; - } else if (parsers.isValidPropertyValue("border-top-color", name, globalObject)) { - if (parsedValues.has("border-top-color")) { + } else if (parsers.isValidPropertyValue(BORDER_TOP_COLOR, name, globalObject)) { + if (parsedValues.has(BORDER_TOP_COLOR)) { return; } - parsedValues.set("border-top-color", name); + parsedValues.set(BORDER_TOP_COLOR, name); break; } return; @@ -119,15 +124,15 @@ module.exports.parse = function parse(v, opt = {}) { if (parsedValues.size) { const keys = module.exports.shorthandFor.keys(); const obj = { - "border-top-width": "medium" + [BORDER_TOP_WIDTH]: "medium" }; for (const key of keys) { if (parsedValues.has(key)) { const parsedValue = parsedValues.get(key); if (parsedValue !== module.exports.initialValues.get(key)) { obj[key] = parsedValues.get(key); - if (obj["border-top-width"] && obj["border-top-width"] === "medium") { - delete obj["border-top-width"]; + if (obj[BORDER_TOP_WIDTH] && obj[BORDER_TOP_WIDTH] === "medium") { + delete obj[BORDER_TOP_WIDTH]; } } } @@ -140,26 +145,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (val || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderTopColor.js b/lib/properties/borderTopColor.js index a1f54879..acec8d3e 100644 --- a/lib/properties/borderTopColor.js +++ b/lib/properties/borderTopColor.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-top-color"; -const lineShorthand = "border-color"; -const positionShorthand = "border-top"; -const shorthand = "border"; +const PROPERTY = "border-top-color"; +const LINE_SHORTHAND = "border-color"; +const POSITION_SHORTHAND = "border-top"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,7 +22,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -36,30 +38,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderTopStyle.js b/lib/properties/borderTopStyle.js index d2ce7a93..6cb92863 100644 --- a/lib/properties/borderTopStyle.js +++ b/lib/properties/borderTopStyle.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-top-style"; -const lineShorthand = "border-style"; -const positionShorthand = "border-top"; -const shorthand = "border"; +const PROPERTY = "border-top-style"; +const LINE_SHORTHAND = "border-style"; +const POSITION_SHORTHAND = "border-top"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,8 +22,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -35,30 +37,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderTopWidth.js b/lib/properties/borderTopWidth.js index f916afcd..d7fbd296 100644 --- a/lib/properties/borderTopWidth.js +++ b/lib/properties/borderTopWidth.js @@ -1,18 +1,20 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "border-top-width"; -const lineShorthand = "border-width"; -const positionShorthand = "border-top"; -const shorthand = "border"; +const PROPERTY = "border-top-width"; +const LINE_SHORTHAND = "border-width"; +const POSITION_SHORTHAND = "border-top"; +const SHORTHAND = "border"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -49,30 +51,30 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const linePriority = this._priorities.get(lineShorthand); - const positionPriority = this._priorities.get(positionShorthand); - let priority = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const linePriority = this._priorities.get(LINE_SHORTHAND); + const positionPriority = this._priorities.get(POSITION_SHORTHAND); + let priority = this._priorities.get(PROPERTY) ?? ""; if ((shorthandPriority || linePriority || positionPriority) && priority) { priority = ""; } - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/borderWidth.js b/lib/properties/borderWidth.js index 9da553d6..736112b3 100644 --- a/lib/properties/borderWidth.js +++ b/lib/properties/borderWidth.js @@ -1,27 +1,33 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const borderTopWidth = require("./borderTopWidth"); const borderRightWidth = require("./borderRightWidth"); const borderBottomWidth = require("./borderBottomWidth"); const borderLeftWidth = require("./borderLeftWidth"); -const property = "border-width"; -const shorthand = "border"; +const PROPERTY = "border-width"; +const SHORTHAND = "border"; +const BORDER_TOP_WIDTH = "border-top-width"; +const BORDER_RIGHT_WIDTH = "border-right-width"; +const BORDER_BOTTOM_WIDTH = "border-bottom-width"; +const BORDER_LEFT_WIDTH = "border-left-width"; module.exports.shorthandFor = new Map([ - ["border-top-width", borderTopWidth], - ["border-right-width", borderRightWidth], - ["border-bottom-width", borderBottomWidth], - ["border-left-width", borderLeftWidth] + [BORDER_TOP_WIDTH, borderTopWidth], + [BORDER_RIGHT_WIDTH, borderRightWidth], + [BORDER_BOTTOM_WIDTH, borderBottomWidth], + [BORDER_LEFT_WIDTH, borderLeftWidth] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const values = parsers.parsePropertyValue(property, v, { + const values = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -34,20 +40,20 @@ module.exports.parse = function parse(v, opt = {}) { for (const value of values) { const { isNumber, name, type, value: itemValue } = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } parsedValues.push(`${name}(${itemValue})`); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { if (values.length !== 1) { return; } return name; } - case "Identifier": { + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -69,20 +75,20 @@ module.exports.parse = function parse(v, opt = {}) { } else if (typeof values === "string") { parsedValues.push(values); } - if (parsedValues.length) { - switch (parsedValues.length) { + const l = parsedValues.length; + if (l) { + const [val1, val2, val3, val4] = parsedValues; + switch (l) { case 1: { return parsedValues; } case 2: { - const [val1, val2] = parsedValues; if (val1 === val2) { return [val1]; } return parsedValues; } case 3: { - const [val1, val2, val3] = parsedValues; if (val1 === val3) { if (val1 === val2) { return [val1]; @@ -92,7 +98,6 @@ module.exports.parse = function parse(v, opt = {}) { return parsedValues; } case 4: { - const [val1, val2, val3, val4] = parsedValues; if (val2 === val4) { if (val1 === val3) { if (val1 === val2) { @@ -113,26 +118,26 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._borderSetter(property, v, ""); + this._borderSetter(PROPERTY, v, ""); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (Array.isArray(val) || typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._borderSetter(property, val, priority); + this._borderSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/bottom.js b/lib/properties/bottom.js index 4bbed837..bf0d5fa6 100644 --- a/lib/properties/bottom.js +++ b/lib/properties/bottom.js @@ -1,30 +1,32 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "bottom"; +const PROPERTY = "bottom"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true }); if (Array.isArray(value) && value.length === 1) { - const [{ isNumber, name, raw, type }] = value; + const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { - if (!isNumber) { - return raw; + case AST_TYPES.CALC: { + if (isNumber) { + return; } - break; + return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -40,24 +42,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/clear.js b/lib/properties/clear.js index 9eb093ba..3746f558 100644 --- a/lib/properties/clear.js +++ b/lib/properties/clear.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "clear"; +const PROPERTY = "clear"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,8 +19,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -32,24 +34,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/clip.js b/lib/properties/clip.js index 9769dbf1..c7255338 100644 --- a/lib/properties/clip.js +++ b/lib/properties/clip.js @@ -3,15 +3,17 @@ // @see https://drafts.fxtf.org/css-masking/#clip-property const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "clip"; +const PROPERTY = "clip"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -19,7 +21,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type, value: itemValue }] = value; switch (type) { - case "Function": { + case AST_TYPES.FUNCTION: { const values = parsers.splitValue(itemValue, { delimiter: "," }); @@ -42,8 +44,8 @@ module.exports.parse = function parse(v, opt = {}) { } return `${name}(${parsedValues.join(", ")})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -57,24 +59,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/color.js b/lib/properties/color.js index cf23c1d2..0d79f4b0 100644 --- a/lib/properties/color.js +++ b/lib/properties/color.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "color"; +const PROPERTY = "color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/display.js b/lib/properties/display.js index efe0a105..416ab699 100644 --- a/lib/properties/display.js +++ b/lib/properties/display.js @@ -1,19 +1,29 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "display"; - -/* keywords */ -const displayOutside = ["block", "inline", "run-in"]; -const displayFlow = ["flow", "flow-root"]; +const PROPERTY = "display"; +const BLOCK = "block"; +const FLEX = "flex"; +const FLOW = "flow"; +const FLOW_ROOT = "flow-root"; +const GRID = "grid"; +const INLINE = "inline"; +const LIST_ITEM = "list-item"; +const RUBY = "ruby"; +const RUN_IN = "run-in"; +const TABLE = "table"; +const DISPLAY_OUTSIDE = [BLOCK, INLINE, RUN_IN]; +const DISPLAY_FLOW = [FLOW, FLOW_ROOT]; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -23,12 +33,12 @@ module.exports.parse = function parse(v, opt = {}) { case 1: { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Identifier": { - if (name === "flow") { - return "block"; + case AST_TYPES.IDENTIFIER: { + if (name === FLOW) { + return BLOCK; } return name; } @@ -38,81 +48,81 @@ module.exports.parse = function parse(v, opt = {}) { } case 2: { const [part1, part2] = value; - const val1 = part1.type === "Identifier" && part1.name; - const val2 = part2.type === "Identifier" && part2.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; + const val2 = part2.type === AST_TYPES.IDENTIFIER && part2.name; if (val1 && val2) { let outerValue = ""; let innerValue = ""; - if (val1 === "list-item") { + if (val1 === LIST_ITEM) { outerValue = val2; innerValue = val1; - } else if (val2 === "list-item") { + } else if (val2 === LIST_ITEM) { outerValue = val1; innerValue = val2; - } else if (displayOutside.includes(val1)) { + } else if (DISPLAY_OUTSIDE.includes(val1)) { outerValue = val1; innerValue = val2; - } else if (displayOutside.includes(val2)) { + } else if (DISPLAY_OUTSIDE.includes(val2)) { outerValue = val2; innerValue = val1; } - if (innerValue === "list-item") { + if (innerValue === LIST_ITEM) { switch (outerValue) { - case "block": - case "flow": { + case BLOCK: + case FLOW: { return innerValue; } - case "flow-root": - case "inline": - case "run-in": { + case FLOW_ROOT: + case INLINE: + case RUN_IN: { return `${outerValue} ${innerValue}`; } default: } - } else if (outerValue === "block") { + } else if (outerValue === BLOCK) { switch (innerValue) { - case "flow": { + case FLOW: { return outerValue; } - case "flow-root": - case "flex": - case "grid": - case "table": { + case FLOW_ROOT: + case FLEX: + case GRID: + case TABLE: { return innerValue; } - case "ruby": { + case RUBY: { return `${outerValue} ${innerValue}`; } default: } - } else if (outerValue === "inline") { + } else if (outerValue === INLINE) { switch (innerValue) { - case "flow": { + case FLOW: { return outerValue; } - case "flow-root": { + case FLOW_ROOT: { return `${outerValue}-block`; } - case "flex": - case "grid": - case "table": { + case FLEX: + case GRID: + case TABLE: { return `${outerValue}-${innerValue}`; } - case "ruby": { + case RUBY: { return innerValue; } default: } - } else if (outerValue === "run-in") { + } else if (outerValue === RUN_IN) { switch (innerValue) { - case "flow": { + case FLOW: { return outerValue; } - case "flow-root": - case "flex": - case "grid": - case "table": - case "ruby": { + case FLOW_ROOT: + case FLEX: + case GRID: + case TABLE: + case RUBY: { return `${outerValue} ${innerValue}`; } default: @@ -123,52 +133,52 @@ module.exports.parse = function parse(v, opt = {}) { } case 3: { const [part1, part2, part3] = value; - const val1 = part1.type === "Identifier" && part1.name; - const val2 = part2.type === "Identifier" && part2.name; - const val3 = part3.type === "Identifier" && part3.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; + const val2 = part2.type === AST_TYPES.IDENTIFIER && part2.name; + const val3 = part3.type === AST_TYPES.IDENTIFIER && part3.name; if (val1 && val2 && part3) { let outerValue = ""; let flowValue = ""; let listItemValue = ""; - if (val1 === "list-item") { + if (val1 === LIST_ITEM) { listItemValue = val1; - if (displayFlow.includes(val2)) { + if (DISPLAY_FLOW.includes(val2)) { flowValue = val2; outerValue = val3; - } else if (displayFlow.includes(val3)) { + } else if (DISPLAY_FLOW.includes(val3)) { flowValue = val3; outerValue = val2; } - } else if (val2 === "list-item") { + } else if (val2 === LIST_ITEM) { listItemValue = val2; - if (displayFlow.includes(val1)) { + if (DISPLAY_FLOW.includes(val1)) { flowValue = val1; outerValue = val3; - } else if (displayFlow.includes(val3)) { + } else if (DISPLAY_FLOW.includes(val3)) { flowValue = val3; outerValue = val1; } - } else if (val3 === "list-item") { + } else if (val3 === LIST_ITEM) { listItemValue = val3; - if (displayFlow.includes(val1)) { + if (DISPLAY_FLOW.includes(val1)) { flowValue = val1; outerValue = val2; - } else if (displayFlow.includes(val2)) { + } else if (DISPLAY_FLOW.includes(val2)) { flowValue = val2; outerValue = val1; } } if (outerValue && flowValue && listItemValue) { switch (outerValue) { - case "block": { - if (flowValue === "flow") { + case BLOCK: { + if (flowValue === FLOW) { return listItemValue; } return `${flowValue} ${listItemValue}`; } - case "inline": - case "run-in": { - if (flowValue === "flow") { + case INLINE: + case RUN_IN: { + if (flowValue === FLOW) { return `${outerValue} ${listItemValue}`; } return `${outerValue} ${flowValue} ${listItemValue}`; @@ -189,24 +199,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/flex.js b/lib/properties/flex.js index b43e6d9b..e91ea1dc 100644 --- a/lib/properties/flex.js +++ b/lib/properties/flex.js @@ -1,131 +1,136 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const flexGrow = require("./flexGrow"); const flexShrink = require("./flexShrink"); const flexBasis = require("./flexBasis"); -const property = "flex"; +const PROPERTY = "flex"; +const FLEX_GROW = "flex-grow"; +const FLEX_SHRINK = "flex-shrink"; +const FLEX_BASIS = "flex-basis"; module.exports.initialValues = new Map([ - ["flex-grow", "0"], - ["flex-shrink", "1"], - ["flex-basis", "auto"] + [FLEX_GROW, "0"], + [FLEX_SHRINK, "1"], + [FLEX_BASIS, "auto"] ]); module.exports.shorthandFor = new Map([ - ["flex-grow", flexGrow], - ["flex-shrink", flexShrink], - ["flex-basis", flexBasis] + [FLEX_GROW, flexGrow], + [FLEX_SHRINK, flexShrink], + [FLEX_BASIS, flexBasis] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true }); if (Array.isArray(value) && value.length) { const flex = { - "flex-grow": "1", - "flex-shrink": "1", - "flex-basis": "0%" + [FLEX_GROW]: "1", + [FLEX_SHRINK]: "1", + [FLEX_BASIS]: "0%" }; if (value.length === 1) { const [{ isNumber, name, type, unit, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { - flex["flex-grow"] = `${name}(${itemValue})`; + flex[FLEX_GROW] = `${name}(${itemValue})`; return flex; } - flex["flex-basis"] = `${name}(${itemValue})`; + flex[FLEX_BASIS] = `${name}(${itemValue})`; return flex; } - case "Dimension": { - flex["flex-basis"] = `${itemValue}${unit}`; + case AST_TYPES.DIMENSION: { + flex[FLEX_BASIS] = `${itemValue}${unit}`; return flex; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } - case "Identifier": { + case AST_TYPES.IDENTIFIER: { if (name === "none") { return { - "flex-grow": "0", - "flex-shrink": "0", - "flex-basis": "auto" + [FLEX_GROW]: "0", + [FLEX_SHRINK]: "0", + [FLEX_BASIS]: "auto" }; } - flex["flex-basis"] = name; + flex[FLEX_BASIS] = name; return flex; } - case "Number": { - flex["flex-grow"] = itemValue; + case AST_TYPES.NUMBER: { + flex[FLEX_GROW] = itemValue; return flex; } - case "Percentage": { - flex["flex-basis"] = `${itemValue}%`; + case AST_TYPES.PERCENTAGE: { + flex[FLEX_BASIS] = `${itemValue}%`; return flex; } default: } } else { const [val1, val2, val3] = value; - if (val1.type === "Calc" && val1.isNumber) { - flex["flex-grow"] = `${val1.name}(${val1.value})`; - } else if (val1.type === "Number") { - flex["flex-grow"] = val1.value; + if (val1.type === AST_TYPES.CALC && val1.isNumber) { + flex[FLEX_GROW] = `${val1.name}(${val1.value})`; + } else if (val1.type === AST_TYPES.NUMBER) { + flex[FLEX_GROW] = val1.value; } else { return; } if (val3) { - if (val2.type === "Calc" && val2.isNumber) { - flex["flex-shrink"] = `${val2.name}(${val2.value})`; - } else if (val2.type === "Number") { - flex["flex-shrink"] = val2.value; + if (val2.type === AST_TYPES.CALC && val2.isNumber) { + flex[FLEX_SHRINK] = `${val2.name}(${val2.value})`; + } else if (val2.type === AST_TYPES.NUMBER) { + flex[FLEX_SHRINK] = val2.value; } else { return; } - if (val3.type === "GlobalKeyword" || val3.type === "Identifier") { - flex["flex-basis"] = val3.name; - } else if (val3.type === "Calc" && !val3.isNumber) { - flex["flex-basis"] = `${val3.name}(${val3.value})`; - } else if (val3.type === "Dimension") { - flex["flex-basis"] = `${val3.value}${val3.unit}`; - } else if (val3.type === "Percentage") { - flex["flex-basis"] = `${val3.value}%`; + if (val3.type === AST_TYPES.GLOBAL_KEY || val3.type === AST_TYPES.IDENTIFIER) { + flex[FLEX_BASIS] = val3.name; + } else if (val3.type === AST_TYPES.CALC && !val3.isNumber) { + flex[FLEX_BASIS] = `${val3.name}(${val3.value})`; + } else if (val3.type === AST_TYPES.DIMENSION) { + flex[FLEX_BASIS] = `${val3.value}${val3.unit}`; + } else if (val3.type === AST_TYPES.PERCENTAGE) { + flex[FLEX_BASIS] = `${val3.value}%`; } else { return; } } else { switch (val2.type) { - case "Calc": { + case AST_TYPES.CALC: { if (val2.isNumber) { - flex["flex-shrink"] = `${val2.name}(${val2.value})`; + flex[FLEX_SHRINK] = `${val2.name}(${val2.value})`; } else { - flex["flex-basis"] = `${val2.name}(${val2.value})`; + flex[FLEX_BASIS] = `${val2.name}(${val2.value})`; } break; } - case "Dimension": { - flex["flex-basis"] = `${val2.value}${val2.unit}`; + case AST_TYPES.DIMENSION: { + flex[FLEX_BASIS] = `${val2.value}${val2.unit}`; break; } - case "Identifier": { - flex["flex-basis"] = val2.name; + case AST_TYPES.IDENTIFIER: { + flex[FLEX_BASIS] = val2.name; break; } - case "Number": { - flex["flex-shrink"] = val2.value; + case AST_TYPES.NUMBER: { + flex[FLEX_SHRINK] = val2.value; break; } - case "Percentage": { - flex["flex-basis"] = `${val2.value}%`; + case AST_TYPES.PERCENTAGE: { + flex[FLEX_BASIS] = `${val2.value}%`; break; } default: { @@ -144,37 +149,37 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, ""); } - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); - const priority = this._priorities.get(property) ?? ""; + const priority = this._priorities.get(PROPERTY) ?? ""; if (typeof val === "string") { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, val, priority); } - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } else if (val) { const values = []; for (const [longhand, value] of Object.entries(val)) { values.push(value); this._setProperty(longhand, value, priority); } - this._setProperty(property, values.join(" "), priority); + this._setProperty(PROPERTY, values.join(" "), priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/flexBasis.js b/lib/properties/flexBasis.js index d1f20b83..25352e13 100644 --- a/lib/properties/flexBasis.js +++ b/lib/properties/flexBasis.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "flex-basis"; -const shorthand = "flex"; +const PROPERTY = "flex-basis"; +const SHORTHAND = "flex"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -18,14 +20,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -41,27 +43,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._flexBoxSetter(property, val, priority, shorthand); + this._flexBoxSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/flexGrow.js b/lib/properties/flexGrow.js index dea336fd..12a58858 100644 --- a/lib/properties/flexGrow.js +++ b/lib/properties/flexGrow.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "flex-grow"; -const shorthand = "flex"; +const PROPERTY = "flex-grow"; +const SHORTHAND = "flex"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue("flex-grow", v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -18,13 +20,13 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return `${name}(${itemValue})`; } break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -40,27 +42,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._flexBoxSetter(property, val, priority, shorthand); + this._flexBoxSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/flexShrink.js b/lib/properties/flexShrink.js index e3963b6b..ee166401 100644 --- a/lib/properties/flexShrink.js +++ b/lib/properties/flexShrink.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "flex-shrink"; -const shorthand = "flex"; +const PROPERTY = "flex-shrink"; +const SHORTHAND = "flex"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -18,13 +20,13 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return `${name}(${itemValue})`; } break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -40,27 +42,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._flexBoxSetter(property, val, priority, shorthand); + this._flexBoxSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/float.js b/lib/properties/float.js index 60afc6b6..17e8bfa3 100644 --- a/lib/properties/float.js +++ b/lib/properties/float.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "float"; +const PROPERTY = "float"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,8 +19,8 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: @@ -32,24 +34,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/floodColor.js b/lib/properties/floodColor.js index 68900d1b..337ab706 100644 --- a/lib/properties/floodColor.js +++ b/lib/properties/floodColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "flood-color"; +const PROPERTY = "flood-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/font.js b/lib/properties/font.js index 1153b711..186d014d 100644 --- a/lib/properties/font.js +++ b/lib/properties/font.js @@ -1,6 +1,7 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const fontStyle = require("./fontStyle"); const fontVariant = require("./fontVariant"); const fontWeight = require("./fontWeight"); @@ -8,25 +9,32 @@ const fontSize = require("./fontSize"); const lineHeight = require("./lineHeight"); const fontFamily = require("./fontFamily"); -const property = "font"; +const PROPERTY = "font"; +const FONT_STYLE = "font-style"; +const FONT_VARIANT = "font-variant"; +const FONT_WEIGHT = "font-weight"; +const FONT_SIZE = "font-size"; +const LINE_HEIGHT = "line-height"; +const FONT_FAMILY = "font-family"; module.exports.shorthandFor = new Map([ - ["font-style", fontStyle], - ["font-variant", fontVariant], - ["font-weight", fontWeight], - ["font-size", fontSize], - ["line-height", lineHeight], - ["font-family", fontFamily] + [FONT_STYLE, fontStyle], + [FONT_VARIANT, fontVariant], + [FONT_WEIGHT, fontWeight], + [FONT_SIZE, fontSize], + [LINE_HEIGHT, lineHeight], + [FONT_FAMILY, fontFamily] ]); module.exports.parse = function parse(v, opt = {}) { + const { AST_TYPES } = constants; const { globalObject, options } = opt; if (v === "") { return v; } else if (parsers.hasCalcFunc(v)) { v = parsers.resolveCalc(v, opt); } - if (!parsers.isValidPropertyValue(property, v, globalObject)) { + if (!parsers.isValidPropertyValue(PROPERTY, v, globalObject)) { return; } const [fontBlock, ...families] = parsers.splitValue(v, { @@ -36,9 +44,9 @@ module.exports.parse = function parse(v, opt = {}) { delimiter: "/" }); const font = { - "font-style": "normal", - "font-variant": "normal", - "font-weight": "normal" + [FONT_STYLE]: "normal", + [FONT_VARIANT]: "normal", + [FONT_WEIGHT]: "normal" }; const fontFamilies = new Set(); if (fontBlockB) { @@ -64,14 +72,14 @@ module.exports.parse = function parse(v, opt = {}) { return; } const parts = parsers.splitValue(fontBlockA.trim()); - const properties = ["font-style", "font-variant", "font-weight", "font-size"]; + const properties = [FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, FONT_SIZE]; for (const part of parts) { if (part === "normal") { continue; } else { for (const longhand of properties) { switch (longhand) { - case "font-size": { + case FONT_SIZE: { const parsedValue = fontSize.parse(part, { globalObject, options @@ -81,8 +89,8 @@ module.exports.parse = function parse(v, opt = {}) { } break; } - case "font-style": - case "font-weight": { + case FONT_STYLE: + case FONT_WEIGHT: { if (font[longhand] === "normal") { const longhandItem = module.exports.shorthandFor.get(longhand); const parsedValue = longhandItem.parse(part, { @@ -95,7 +103,7 @@ module.exports.parse = function parse(v, opt = {}) { } break; } - case "font-variant": { + case FONT_VARIANT: { if (font[longhand] === "normal") { const parsedValue = fontVariant.parse(part, { globalObject, @@ -116,8 +124,8 @@ module.exports.parse = function parse(v, opt = {}) { } } } - if (Object.hasOwn(font, "font-size")) { - font["line-height"] = lineHeightB; + if (Object.hasOwn(font, FONT_SIZE)) { + font[LINE_HEIGHT] = lineHeightB; } else { return; } @@ -125,27 +133,27 @@ module.exports.parse = function parse(v, opt = {}) { const revParts = parsers.splitValue(fontBlockA.trim()).toReversed(); if (revParts.length === 1) { const [part] = revParts; - const value = parsers.parsePropertyValue(property, part, { + const value = parsers.parsePropertyValue(PROPERTY, part, { globalObject, options, inArray: true }); if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; - if (type === "GlobalKeyword") { + if (type === AST_TYPES.GLOBAL_KEY) { return { - "font-style": name, - "font-variant": name, - "font-weight": name, - "font-size": name, - "line-height": name, - "font-family": name + [FONT_STYLE]: name, + [FONT_VARIANT]: name, + [FONT_WEIGHT]: name, + [FONT_SIZE]: name, + [LINE_HEIGHT]: name, + [FONT_FAMILY]: name }; } } return; } - const properties = ["font-style", "font-variant", "font-weight", "line-height"]; + const properties = [FONT_STYLE, FONT_VARIANT, FONT_WEIGHT, LINE_HEIGHT]; for (const longhand of properties) { font[longhand] = "normal"; } @@ -158,9 +166,9 @@ module.exports.parse = function parse(v, opt = {}) { } else { for (const longhand of properties) { switch (longhand) { - case "font-style": - case "font-weight": - case "line-height": { + case FONT_STYLE: + case FONT_WEIGHT: + case LINE_HEIGHT: { if (font[longhand] === "normal") { const longhandItem = module.exports.shorthandFor.get(longhand); const parsedValue = longhandItem.parse(part, { @@ -173,7 +181,7 @@ module.exports.parse = function parse(v, opt = {}) { } break; } - case "font-variant": { + case FONT_VARIANT: { if (font[longhand] === "normal") { const parsedValue = fontVariant.parse(part, { globalObject, @@ -220,7 +228,7 @@ module.exports.parse = function parse(v, opt = {}) { caseSensitive: true }); if (fontSizeA && family) { - font["font-size"] = fontSizeA; + font[FONT_SIZE] = fontSizeA; fontFamilies.add(fontFamily.parse(family)); } else { return; @@ -238,7 +246,7 @@ module.exports.parse = function parse(v, opt = {}) { return; } } - font["font-family"] = [...fontFamilies].join(", "); + font[FONT_FAMILY] = [...fontFamilies].join(", "); return font; }; @@ -246,14 +254,14 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (v === "" || parsers.hasVarFunc(v)) { for (const [key] of module.exports.shorthandFor) { this._setProperty(key, ""); } - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const obj = module.exports.parse(v, { globalObject: this._global, @@ -262,14 +270,14 @@ module.exports.definition = { if (!obj) { return; } - const priority = this._priorities.get(property) ?? ""; + const priority = this._priorities.get(PROPERTY) ?? ""; const str = new Set(); for (const [key] of module.exports.shorthandFor) { const val = obj[key]; if (typeof val === "string") { this._setProperty(key, val, priority); if (val && val !== "normal" && !str.has(val)) { - if (key === "line-height") { + if (key === LINE_HEIGHT) { str.add(`/ ${val}`); } else { str.add(val); @@ -277,11 +285,11 @@ module.exports.definition = { } } } - this._setProperty(property, [...str].join(" "), priority); + this._setProperty(PROPERTY, [...str].join(" "), priority); } }, get() { - const val = this.getPropertyValue(property); + const val = this.getPropertyValue(PROPERTY); if (parsers.hasVarFunc(val)) { return val; } @@ -292,7 +300,7 @@ module.exports.definition = { return ""; } if (v && v !== "normal" && !str.has(v)) { - if (key === "line-height") { + if (key === LINE_HEIGHT) { str.add(`/ ${v}`); } else { str.add(`${v}`); diff --git a/lib/properties/fontFamily.js b/lib/properties/fontFamily.js index 29733e3b..ff943a53 100644 --- a/lib/properties/fontFamily.js +++ b/lib/properties/fontFamily.js @@ -1,21 +1,23 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "font-family"; -const shorthand = "font"; +const PROPERTY = "font-family"; +const SHORTHAND = "font"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v, { delimiter: "," }); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, caseSensitive: true, @@ -25,18 +27,18 @@ module.exports.parse = function parse(v, opt = {}) { if (value.length === 1) { const [{ name, type, value: itemValue }] = value; switch (type) { - case "Function": { + case AST_TYPES.FUNCTION: { parsedValues.push(`${name}(${itemValue})`); break; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { if (name !== "undefined") { parsedValues.push(name); } break; } - case "String": { + case AST_TYPES.STRING: { const parsedValue = itemValue.replaceAll("\\", "").replaceAll('"', '\\"'); parsedValues.push(`"${parsedValue}"`); break; @@ -49,7 +51,7 @@ module.exports.parse = function parse(v, opt = {}) { const parts = []; for (const item of value) { const { name, type } = item; - if (type === "Identifier") { + if (type === AST_TYPES.IDENTIFIER) { parts.push(name); } else { return; @@ -73,27 +75,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/fontSize.js b/lib/properties/fontSize.js index bf7b74c8..14dd9e7c 100644 --- a/lib/properties/fontSize.js +++ b/lib/properties/fontSize.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "font-size"; -const shorthand = "font"; +const PROPERTY = "font-size"; +const SHORTHAND = "font"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -18,14 +20,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -47,27 +49,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/fontStyle.js b/lib/properties/fontStyle.js index 99554be6..aebe2529 100644 --- a/lib/properties/fontStyle.js +++ b/lib/properties/fontStyle.js @@ -1,33 +1,36 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "font-style"; -const shorthand = "font"; +const PROPERTY = "font-style"; +const SHORTHAND = "font"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true }); - if (Array.isArray(value) && value.length) { - if (value.length === 1) { + const l = Array.isArray(value) ? value.length : 0; + if (l) { + if (l === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: } - } else if (value.length === 2) { + } else if (l === 2) { const [part1, part2] = value; - const val1 = part1.type === "Identifier" && part1.name; + const val1 = part1.type === AST_TYPES.IDENTIFIER && part1.name; const val2 = parsers.parseAngle([part2], options); if (val1 && val1 === "oblique" && val2) { return `${val1} ${val2}`; @@ -42,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/fontVariant.js b/lib/properties/fontVariant.js index 84258369..42dff663 100644 --- a/lib/properties/fontVariant.js +++ b/lib/properties/fontVariant.js @@ -1,19 +1,21 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "font-variant"; -const shorthand = "font"; +const PROPERTY = "font-variant"; +const SHORTHAND = "font"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; const values = parsers.splitValue(v); const parsedValues = []; for (const val of values) { - const value = parsers.parsePropertyValue(property, val, { + const value = parsers.parsePropertyValue(PROPERTY, val, { globalObject, options, inArray: true @@ -21,12 +23,12 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type, value: itemValue }] = value; switch (type) { - case "Function": { + case AST_TYPES.FUNCTION: { parsedValues.push(`${name}(${itemValue})`); break; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -38,8 +40,9 @@ module.exports.parse = function parse(v, opt = {}) { parsedValues.push(value); } } - if (parsedValues.length) { - if (parsedValues.length > 1) { + const l = parsedValues.length; + if (l) { + if (l > 1) { if (parsedValues.includes("normal") || parsedValues.includes("none")) { return; } @@ -52,27 +55,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/fontWeight.js b/lib/properties/fontWeight.js index 736ee1dd..bc0c1fff 100644 --- a/lib/properties/fontWeight.js +++ b/lib/properties/fontWeight.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "font-weight"; -const shorthand = "font"; +const PROPERTY = "font-weight"; +const SHORTHAND = "font"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -18,14 +20,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return `${name}(${itemValue})`; } break; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -51,27 +53,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/height.js b/lib/properties/height.js index cfcd2533..f8eeb545 100644 --- a/lib/properties/height.js +++ b/lib/properties/height.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "height"; +const PROPERTY = "height"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,14 +19,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -40,24 +42,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/left.js b/lib/properties/left.js index 3350977c..bc727b7d 100644 --- a/lib/properties/left.js +++ b/lib/properties/left.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "left"; +const PROPERTY = "left"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,14 +19,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -40,24 +42,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/lightingColor.js b/lib/properties/lightingColor.js index 0d7f1e4b..e1b088c4 100644 --- a/lib/properties/lightingColor.js +++ b/lib/properties/lightingColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "lighting-color"; +const PROPERTY = "lighting-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/lineHeight.js b/lib/properties/lineHeight.js index f7a75d1c..3b49f985 100644 --- a/lib/properties/lineHeight.js +++ b/lib/properties/lineHeight.js @@ -1,16 +1,18 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "line-height"; -const shorthand = "font"; +const PROPERTY = "line-height"; +const SHORTHAND = "font"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -24,14 +26,14 @@ module.exports.parse = function parse(v, opt = {}) { parseOpt[key] = val; } switch (type) { - case "Calc": { + case AST_TYPES.CALC: { return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } - case "Number": { + case AST_TYPES.NUMBER: { return parsers.parseNumber(value, parseOpt); } default: { @@ -47,27 +49,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._setProperty(property, val, priority); + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/margin.js b/lib/properties/margin.js index 6f87f87f..2aefd4c3 100644 --- a/lib/properties/margin.js +++ b/lib/properties/margin.js @@ -1,28 +1,34 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const marginTop = require("./marginTop"); const marginRight = require("./marginRight"); const marginBottom = require("./marginBottom"); const marginLeft = require("./marginLeft"); -const property = "margin"; +const PROPERTY = "margin"; +const MARGIN_TOP = "margin-top"; +const MARGIN_RIGHT = "margin-right"; +const MARGIN_BOTTOM = "margin-bottom"; +const MARGIN_LEFT = "margin-left"; module.exports.position = "edges"; module.exports.shorthandFor = new Map([ - ["margin-top", marginTop], - ["margin-right", marginRight], - ["margin-bottom", marginBottom], - ["margin-left", marginLeft] + [MARGIN_TOP, marginTop], + [MARGIN_RIGHT, marginRight], + [MARGIN_BOTTOM, marginBottom], + [MARGIN_LEFT, marginLeft] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const values = parsers.parsePropertyValue(property, v, { + const values = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -35,21 +41,21 @@ module.exports.parse = function parse(v, opt = {}) { for (const value of values) { const { isNumber, name, type, value: itemValue } = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } parsedValues.push(`${name}(${itemValue})`); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { if (values.length !== 1) { return; } parsedValues.push(name); break; } - case "Identifier": { + case AST_TYPES.IDENTIFIER: { parsedValues.push(name); break; } @@ -65,8 +71,42 @@ module.exports.parse = function parse(v, opt = {}) { } else if (typeof values === "string") { parsedValues.push(values); } - if (parsedValues.length) { - return parsedValues; + const l = parsedValues.length; + if (l) { + const [val1, val2, val3, val4] = parsedValues; + switch (l) { + case 1: { + return parsedValues; + } + case 2: { + if (val1 === val2) { + return [val1]; + } + return parsedValues; + } + case 3: { + if (val1 === val3) { + if (val1 === val2) { + return [val1]; + } + return [val1, val2]; + } + return parsedValues; + } + case 4: { + if (val2 === val4) { + if (val1 === val3) { + if (val1 === val2) { + return [val1]; + } + return [val1, val2]; + } + return [val1, val2, val3]; + } + return parsedValues; + } + default: + } } }; @@ -74,27 +114,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, ""); } - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (Array.isArray(val) || typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._positionShorthandSetter(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._positionShorthandSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/marginBottom.js b/lib/properties/marginBottom.js index 62f5fcdf..066d9fba 100644 --- a/lib/properties/marginBottom.js +++ b/lib/properties/marginBottom.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "margin-bottom"; -const shorthand = "margin"; +const PROPERTY = "margin-bottom"; +const SHORTHAND = "margin"; module.exports.position = "bottom"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/marginLeft.js b/lib/properties/marginLeft.js index b66e7b11..0bee0b3b 100644 --- a/lib/properties/marginLeft.js +++ b/lib/properties/marginLeft.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "margin-left"; -const shorthand = "margin"; +const PROPERTY = "margin-left"; +const SHORTHAND = "margin"; module.exports.position = "left"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/marginRight.js b/lib/properties/marginRight.js index 6d1696c3..e16fdfc0 100644 --- a/lib/properties/marginRight.js +++ b/lib/properties/marginRight.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "margin-right"; -const shorthand = "margin"; +const PROPERTY = "margin-right"; +const SHORTHAND = "margin"; module.exports.position = "right"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/marginTop.js b/lib/properties/marginTop.js index 59257856..06a8fe0e 100644 --- a/lib/properties/marginTop.js +++ b/lib/properties/marginTop.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "margin-top"; -const shorthand = "margin"; +const PROPERTY = "margin-top"; +const SHORTHAND = "margin"; module.exports.position = "top"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,14 +22,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -43,27 +45,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/opacity.js b/lib/properties/opacity.js index 6d9c6ed1..31b6b1d0 100644 --- a/lib/properties/opacity.js +++ b/lib/properties/opacity.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "opacity"; +const PROPERTY = "opacity"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,17 +19,17 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } - case "Number": { + case AST_TYPES.NUMBER: { return parsers.parseNumber(value, options); } - case "Percentage": { + case AST_TYPES.PERCENTAGE: { return parsers.parsePercentage(value, options); } default: @@ -41,24 +43,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/outlineColor.js b/lib/properties/outlineColor.js index 761fad94..c627e8f5 100644 --- a/lib/properties/outlineColor.js +++ b/lib/properties/outlineColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "outline-color"; +const PROPERTY = "outline-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/padding.js b/lib/properties/padding.js index 77a83b22..55ac146b 100644 --- a/lib/properties/padding.js +++ b/lib/properties/padding.js @@ -1,28 +1,34 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); const paddingTop = require("./paddingTop"); const paddingRight = require("./paddingRight"); const paddingBottom = require("./paddingBottom"); const paddingLeft = require("./paddingLeft"); -const property = "padding"; +const PROPERTY = "padding"; +const PADDING_TOP = "padding-top"; +const PADDING_RIGHT = "padding-right"; +const PADDING_BOTTOM = "padding-bottom"; +const PADDING_LEFT = "padding-left"; module.exports.position = "edges"; module.exports.shorthandFor = new Map([ - ["padding-top", paddingTop], - ["padding-right", paddingRight], - ["padding-bottom", paddingBottom], - ["padding-left", paddingLeft] + [PADDING_TOP, paddingTop], + [PADDING_RIGHT, paddingRight], + [PADDING_BOTTOM, paddingBottom], + [PADDING_LEFT, paddingLeft] ]); module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const values = parsers.parsePropertyValue(property, v, { + const values = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -35,14 +41,14 @@ module.exports.parse = function parse(v, opt = {}) { for (const value of values) { const { isNumber, name, type, value: itemValue } = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } parsedValues.push(`${name}(${itemValue})`); break; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { if (values.length !== 1) { return; } @@ -67,8 +73,42 @@ module.exports.parse = function parse(v, opt = {}) { } else if (typeof values === "string") { parsedValues.push(values); } - if (parsedValues.length) { - return parsedValues; + const l = parsedValues.length; + if (l) { + const [val1, val2, val3, val4] = parsedValues; + switch (l) { + case 1: { + return parsedValues; + } + case 2: { + if (val1 === val2) { + return [val1]; + } + return parsedValues; + } + case 3: { + if (val1 === val3) { + if (val1 === val2) { + return [val1]; + } + return [val1, val2]; + } + return parsedValues; + } + case 4: { + if (val2 === val4) { + if (val1 === val3) { + if (val1 === val2) { + return [val1]; + } + return [val1, val2]; + } + return [val1, val2, val3]; + } + return parsedValues; + } + default: + } } }; @@ -76,27 +116,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { for (const [longhand] of module.exports.shorthandFor) { this._setProperty(longhand, ""); } - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (Array.isArray(val) || typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._positionShorthandSetter(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._positionShorthandSetter(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/paddingBottom.js b/lib/properties/paddingBottom.js index f8f2f58a..0596dc79 100644 --- a/lib/properties/paddingBottom.js +++ b/lib/properties/paddingBottom.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "padding-bottom"; -const shorthand = "padding"; +const PROPERTY = "padding-bottom"; +const SHORTHAND = "padding"; module.exports.position = "bottom"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,13 +22,13 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -48,27 +50,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/paddingLeft.js b/lib/properties/paddingLeft.js index 69217d08..16c2acd1 100644 --- a/lib/properties/paddingLeft.js +++ b/lib/properties/paddingLeft.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "padding-left"; -const shorthand = "padding"; +const PROPERTY = "padding-left"; +const SHORTHAND = "padding"; module.exports.position = "left"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,13 +22,13 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -48,27 +50,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/paddingRight.js b/lib/properties/paddingRight.js index 94de1d59..4b164d04 100644 --- a/lib/properties/paddingRight.js +++ b/lib/properties/paddingRight.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "padding-right"; -const shorthand = "padding"; +const PROPERTY = "padding-right"; +const SHORTHAND = "padding"; module.exports.position = "right"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,13 +22,13 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -48,27 +50,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/paddingTop.js b/lib/properties/paddingTop.js index 2f9d9976..7538745c 100644 --- a/lib/properties/paddingTop.js +++ b/lib/properties/paddingTop.js @@ -1,9 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "padding-top"; -const shorthand = "padding"; +const PROPERTY = "padding-top"; +const SHORTHAND = "padding"; module.exports.position = "top"; @@ -11,8 +12,9 @@ module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -20,13 +22,13 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -48,27 +50,27 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(shorthand, ""); - this._setProperty(property, v); + this._setProperty(SHORTHAND, ""); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const shorthandPriority = this._priorities.get(shorthand); - const prior = this._priorities.get(property) ?? ""; + const shorthandPriority = this._priorities.get(SHORTHAND); + const prior = this._priorities.get(PROPERTY) ?? ""; const priority = shorthandPriority && prior ? "" : prior; - this._positionLonghandSetter(property, val, priority, shorthand); + this._positionLonghandSetter(PROPERTY, val, priority, SHORTHAND); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/right.js b/lib/properties/right.js index 987ce751..271ce332 100644 --- a/lib/properties/right.js +++ b/lib/properties/right.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "right"; +const PROPERTY = "right"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,14 +19,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -40,24 +42,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/stopColor.js b/lib/properties/stopColor.js index 9208499d..0b31db12 100644 --- a/lib/properties/stopColor.js +++ b/lib/properties/stopColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "stop-color"; +const PROPERTY = "stop-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/top.js b/lib/properties/top.js index de65de34..85f83432 100644 --- a/lib/properties/top.js +++ b/lib/properties/top.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "top"; +const PROPERTY = "top"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,14 +19,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -40,24 +42,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitBorderAfterColor.js b/lib/properties/webkitBorderAfterColor.js index 055196b1..c0f2098b 100644 --- a/lib/properties/webkitBorderAfterColor.js +++ b/lib/properties/webkitBorderAfterColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-border-after-color"; +const PROPERTY = "-webkit-border-after-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitBorderBeforeColor.js b/lib/properties/webkitBorderBeforeColor.js index 3991d63d..e415ddb9 100644 --- a/lib/properties/webkitBorderBeforeColor.js +++ b/lib/properties/webkitBorderBeforeColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-border-before-color"; +const PROPERTY = "-webkit-border-before-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitBorderEndColor.js b/lib/properties/webkitBorderEndColor.js index 717cc035..52332692 100644 --- a/lib/properties/webkitBorderEndColor.js +++ b/lib/properties/webkitBorderEndColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-border-end-color"; +const PROPERTY = "-webkit-border-end-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitBorderStartColor.js b/lib/properties/webkitBorderStartColor.js index 9fad8c05..dfdbc891 100644 --- a/lib/properties/webkitBorderStartColor.js +++ b/lib/properties/webkitBorderStartColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-border-start-color"; +const PROPERTY = "-webkit-border-start-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitColumnRuleColor.js b/lib/properties/webkitColumnRuleColor.js index 9f46a0ef..e72631d1 100644 --- a/lib/properties/webkitColumnRuleColor.js +++ b/lib/properties/webkitColumnRuleColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-column-rule-color"; +const PROPERTY = "-webkit-column-rule-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitTapHighlightColor.js b/lib/properties/webkitTapHighlightColor.js index 32a1f5d9..2e412d35 100644 --- a/lib/properties/webkitTapHighlightColor.js +++ b/lib/properties/webkitTapHighlightColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-tap-highlight-color"; +const PROPERTY = "-webkit-tap-highlight-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitTextEmphasisColor.js b/lib/properties/webkitTextEmphasisColor.js index a9c1a195..ddb799bd 100644 --- a/lib/properties/webkitTextEmphasisColor.js +++ b/lib/properties/webkitTextEmphasisColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-text-emphasis-color"; +const PROPERTY = "-webkit-text-emphasis-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitTextFillColor.js b/lib/properties/webkitTextFillColor.js index 856b2bc2..1c7a5fdb 100644 --- a/lib/properties/webkitTextFillColor.js +++ b/lib/properties/webkitTextFillColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-text-fill-color"; +const PROPERTY = "-webkit-text-fill-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/webkitTextStrokeColor.js b/lib/properties/webkitTextStrokeColor.js index b7b074a1..f4569461 100644 --- a/lib/properties/webkitTextStrokeColor.js +++ b/lib/properties/webkitTextStrokeColor.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "-webkit-text-stroke-color"; +const PROPERTY = "-webkit-text-stroke-color"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,7 +19,7 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ name, type }] = value; switch (type) { - case "GlobalKeyword": { + case AST_TYPES.GLOBAL_KEY: { return name; } default: { @@ -33,24 +35,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/properties/width.js b/lib/properties/width.js index e080c619..6aaede88 100644 --- a/lib/properties/width.js +++ b/lib/properties/width.js @@ -1,15 +1,17 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("../utils/constants"); -const property = "width"; +const PROPERTY = "width"; module.exports.parse = function parse(v, opt = {}) { if (v === "") { return v; } + const { AST_TYPES } = constants; const { globalObject, options } = opt; - const value = parsers.parsePropertyValue(property, v, { + const value = parsers.parsePropertyValue(PROPERTY, v, { globalObject, options, inArray: true @@ -17,14 +19,14 @@ module.exports.parse = function parse(v, opt = {}) { if (Array.isArray(value) && value.length === 1) { const [{ isNumber, name, type, value: itemValue }] = value; switch (type) { - case "Calc": { + case AST_TYPES.CALC: { if (isNumber) { return; } return `${name}(${itemValue})`; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { return name; } default: { @@ -40,24 +42,24 @@ module.exports.definition = { set(v) { v = parsers.prepareValue(v, this._global); // The value has already been set. - if (this._values.get(property) === v && !this._priorities.get(property)) { + if (this._values.get(PROPERTY) === v && !this._priorities.get(PROPERTY)) { return; } if (parsers.hasVarFunc(v)) { - this._setProperty(property, v); + this._setProperty(PROPERTY, v); } else { const val = module.exports.parse(v, { globalObject: this._global, options: this._options }); if (typeof val === "string") { - const priority = this._priorities.get(property) ?? ""; - this._setProperty(property, val, priority); + const priority = this._priorities.get(PROPERTY) ?? ""; + this._setProperty(PROPERTY, val, priority); } } }, get() { - return this.getPropertyValue(property); + return this.getPropertyValue(PROPERTY); }, enumerable: true, configurable: true diff --git a/lib/utils/constants.js b/lib/utils/constants.js index 06c1aff5..df767544 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -2,8 +2,11 @@ // AST Node types exports.AST_TYPES = Object.freeze({ + ANGLE: "Angle", + CALC: "Calc", DIMENSION: "Dimension", FUNCTION: "Function", + GLOBAL_KEY: "GlobalKeyword", HASH: "Hash", IDENTIFIER: "Identifier", NUMBER: "Number", diff --git a/lib/utils/propertyDescriptors.js b/lib/utils/propertyDescriptors.js index f2f3303f..69871d0e 100644 --- a/lib/utils/propertyDescriptors.js +++ b/lib/utils/propertyDescriptors.js @@ -1,8 +1,10 @@ "use strict"; const parsers = require("../parsers"); +const constants = require("./constants"); exports.getPropertyDescriptor = function getPropertyDescriptor(property) { + const { AST_TYPES } = constants; return { set(v) { const globalObject = this._global; @@ -24,36 +26,36 @@ exports.getPropertyDescriptor = function getPropertyDescriptor(property) { if (Array.isArray(val) && val.length === 1) { const [{ name, raw, type, value: itemValue }] = val; switch (type) { - case "Angle": { + case AST_TYPES.ANGLE: { value = parsers.parseAngle(val, options); break; } - case "Calc": { + case AST_TYPES.CALC: { value = `${name}(${itemValue})`; break; } - case "Dimension": { + case AST_TYPES.DIMENSION: { value = parsers.parseLength(val, options); break; } - case "GlobalKeyword": - case "Identifier": { + case AST_TYPES.GLOBAL_KEY: + case AST_TYPES.IDENTIFIER: { value = name; break; } - case "Number": { + case AST_TYPES.NUMBER: { value = parsers.parseNumber(val, options); break; } - case "Percentage": { + case AST_TYPES.PERCENTAGE: { value = parsers.parsePercentage(val, options); break; } - case "String": { + case AST_TYPES.STRING: { value = parsers.parseString(val, options); break; } - case "Url": { + case AST_TYPES.URL: { value = parsers.parseURL(val, options); break; } diff --git a/test/CSSStyleDeclaration.test.js b/test/CSSStyleDeclaration.test.js index 87109384..cd2d6fe7 100644 --- a/test/CSSStyleDeclaration.test.js +++ b/test/CSSStyleDeclaration.test.js @@ -1362,6 +1362,24 @@ describe("properties", () => { assert.strictEqual(style.getPropertyValue("font-size"), "12px"); }); + it("support global keywords in border-spacing", () => { + const style = new CSSStyleDeclaration(window); + style.borderSpacing = "inherit"; + assert.strictEqual(style.cssText, "border-spacing: inherit;"); + }); + + it("support 1 value in border-spacing", () => { + const style = new CSSStyleDeclaration(window); + style.borderSpacing = "1px"; + assert.strictEqual(style.cssText, "border-spacing: 1px;"); + }); + + it("support 2 values in border-spacing", () => { + const style = new CSSStyleDeclaration(window); + style.borderSpacing = "1px 2px"; + assert.strictEqual(style.cssText, "border-spacing: 1px 2px;"); + }); + it("support non string entries in border-spacing", () => { const style = new CSSStyleDeclaration(window); style.borderSpacing = 0; From 1b372adcc78f11442a6ecaba2fe5ff4c94f0997f Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Mon, 24 Nov 2025 11:04:08 +0900 Subject: [PATCH 23/29] Update generateProperties.js --- scripts/generateProperties.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/generateProperties.js b/scripts/generateProperties.js index 30372f2d..5bbd2865 100644 --- a/scripts/generateProperties.js +++ b/scripts/generateProperties.js @@ -31,7 +31,6 @@ 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 \n`; outFile.write(output); From 3c1a7da9b01e459c43a32b7eb4840a34ba03262c Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Sat, 29 Nov 2025 08:58:25 +0900 Subject: [PATCH 24/29] Require each handler --- lib/CSSStyleDeclaration.js | 9 +++------ lib/handlers/index.js | 12 ------------ 2 files changed, 3 insertions(+), 18 deletions(-) delete mode 100644 lib/handlers/index.js diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 20ea8357..4bde5cfe 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -6,12 +6,9 @@ const generatedProperties = require("./generated/properties"); const propertyDefinitions = require("./generated/propertyDefinitions"); -const { - handleBorder, - handleFlex, - handlePositionShorthand, - handlePositionLonghand -} = require("./handlers"); +const { handleBorder } = require("./handlers/border"); +const { handleFlex } = require("./handlers/flex"); +const { handlePositionShorthand, handlePositionLonghand } = require("./handlers/position"); const { normalizeProperties, prepareProperties, shorthandProperties } = require("./normalize"); const { hasVarFunc, parseCSS, parsePropertyValue, prepareValue } = require("./parsers"); const { getCache, setCache } = require("./utils/cache"); diff --git a/lib/handlers/index.js b/lib/handlers/index.js deleted file mode 100644 index 41ef4a6d..00000000 --- a/lib/handlers/index.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; - -const { handleBorder } = require("./border"); -const { handleFlex } = require("./flex"); -const { handlePositionShorthand, handlePositionLonghand } = require("./position"); - -module.exports = { - handleBorder, - handleFlex, - handlePositionShorthand, - handlePositionLonghand -}; From 8472b501a959752ae1cf05855ef7cee91b009671 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Wed, 3 Dec 2025 23:29:15 +0900 Subject: [PATCH 25/29] Address try-catch-finally --- lib/CSSStyleDeclaration.js | 25 +++++++++++++++++++++---- lib/handlers/border.js | 9 --------- lib/handlers/flex.js | 11 ----------- lib/handlers/position.js | 11 ----------- 4 files changed, 21 insertions(+), 35 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 4bde5cfe..3f841869 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -16,6 +16,9 @@ const { ELEMENT_NODE, NO_MODIFICATION_ALLOWED_ERR } = require("./utils/constants const { getPropertyDescriptor } = require("./utils/propertyDescriptors"); const { asciiLowercase } = require("./utils/strings"); +/* constants */ +const SHORTHAND_PROPS = new Set(["border", "flex", "margin", "padding"]); + /** * @see https://drafts.csswg.org/cssom/#the-cssstyledeclaration-interface */ @@ -111,8 +114,8 @@ class CSSStyleDeclaration { return; } this._clearProperties(); - this._updating = true; try { + this._updating = true; const valueObj = this._parseCSSText(val); if (valueObj?.children) { const properties = this._extractDeclarations(valueObj.children); @@ -120,8 +123,9 @@ class CSSStyleDeclaration { } } catch { return; + } finally { + this._updating = false; } - this._updating = false; this._notifyChange(); } @@ -523,8 +527,21 @@ const internalSetters = { // Assign the internal setters to the prototype. for (const [methodName, handler] of Object.entries(internalSetters)) { // NOTE: 'this' must be bound to the CSSStyleDeclaration instance. - CSSStyleDeclaration.prototype[methodName] = function (...args) { - handler(this, ...args); + CSSStyleDeclaration.prototype[methodName] = function (prop, ...args) { + if (SHORTHAND_PROPS.has(prop)) { + const originalText = typeof this._onChange === "function" ? this.cssText : null; + try { + this._updating = true; + handler(this, prop, ...args); + } catch { + return; + } finally { + this._updating = false; + } + this._notifyChange(originalText); + } else { + handler(this, prop, ...args); + } }; } diff --git a/lib/handlers/border.js b/lib/handlers/border.js index 4bf9beb8..7c71503b 100644 --- a/lib/handlers/border.js +++ b/lib/handlers/border.js @@ -12,12 +12,7 @@ const { borderProperties, prepareBorderProperties } = require("../normalize"); */ exports.handleBorder = (styleDecl, prop, val, prior) => { const properties = new Map(); - let originalText = null; if (prop === "border") { - styleDecl._updating = true; - if (typeof styleDecl._onChange === "function") { - originalText = styleDecl.cssText; - } let priority = ""; if (typeof prior === "string") { priority = prior; @@ -47,8 +42,4 @@ exports.handleBorder = (styleDecl, prop, val, prior) => { const { priority, value } = item; styleDecl._setProperty(property, value, priority); } - if (prop === "border") { - styleDecl._updating = false; - styleDecl._notifyChange(originalText); - } }; diff --git a/lib/handlers/flex.js b/lib/handlers/flex.js index f6280a5a..88944c52 100644 --- a/lib/handlers/flex.js +++ b/lib/handlers/flex.js @@ -16,13 +16,6 @@ exports.handleFlex = (styleDecl, prop, val, prior, shorthandProperty) => { if (!shorthandProperty || !shorthandProperties.has(shorthandProperty)) { return; } - let originalText = null; - if (prop === "flex") { - styleDecl._updating = true; - if (typeof styleDecl._onChange === "function") { - originalText = styleDecl.cssText; - } - } const shorthandPriority = styleDecl._priorities.get(shorthandProperty); styleDecl.removeProperty(shorthandProperty); let priority = ""; @@ -74,8 +67,4 @@ exports.handleFlex = (styleDecl, prop, val, prior, shorthandProperty) => { } } } - if (prop === "flex") { - styleDecl._updating = false; - styleDecl._notifyChange(originalText); - } }; diff --git a/lib/handlers/position.js b/lib/handlers/position.js index 03e31547..9da0e91d 100644 --- a/lib/handlers/position.js +++ b/lib/handlers/position.js @@ -15,13 +15,6 @@ exports.handlePositionShorthand = (styleDecl, prop, val, prior) => { if (!shorthandProperties.has(prop)) { return; } - let originalText = null; - if (prop === "margin" || prop === "padding") { - styleDecl._updating = true; - if (typeof styleDecl._onChange === "function") { - originalText = styleDecl.cssText; - } - } const shorthandValues = []; if (Array.isArray(val)) { shorthandValues.push(...val); @@ -58,10 +51,6 @@ exports.handlePositionShorthand = (styleDecl, prop, val, prior) => { const shorthandValue = getPositionValue(shorthandValues, position); styleDecl._setProperty(prop, shorthandValue, priority); } - if (prop === "margin" || prop === "padding") { - styleDecl._updating = false; - styleDecl._notifyChange(originalText); - } }; /** From 990c07011bad3b19bbc36e3b1d1d4258b81f3a0e Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 4 Dec 2025 00:17:19 +0900 Subject: [PATCH 26/29] Address priority --- lib/CSSStyleDeclaration.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 3f841869..76c77d01 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -198,9 +198,9 @@ class CSSStyleDeclaration { * * @param {string} prop - Property name * @param {string} val - Property value - * @param {string} [prior] - Priority (e.g. "important") + * @param {string} [priority] - Priority (e.g. "important") */ - setProperty(prop, val, prior) { + setProperty(prop, val, priority) { this._checkReadonly(prop); const value = prepareValue(val, this._global); if (value === "") { @@ -208,7 +208,15 @@ class CSSStyleDeclaration { this.removeProperty(prop); return; } - const priority = prior === "important" ? prior : ""; + if (priority) { + if (/^important$/i.test(priority)) { + priority = "important"; + } else { + return; + } + } else { + priority = ""; + } const isCustomProperty = prop.startsWith("--"); if (isCustomProperty) { this._setProperty(prop, value, priority); From 71def32071035dc1999adbe090f91a2bb82b0226 Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 4 Dec 2025 01:01:29 +0900 Subject: [PATCH 27/29] Remove Symbol.iterator --- lib/CSSStyleDeclaration.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 76c77d01..52f1a998 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -264,16 +264,6 @@ class CSSStyleDeclaration { return this._parentRule; } - /** - * Iterator for the declaration block. - * Allows `for (const prop of style)` and `[...style]`. - */ - *[Symbol.iterator]() { - for (let i = 0; i < this._length; i++) { - yield this[i]; - } - } - // Internal methods /** * Checks if the declaration is readonly. From a9be7b4f8695bad7de7190b079a67f1a4822705b Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 4 Dec 2025 01:22:03 +0900 Subject: [PATCH 28/29] Add comment. --- lib/CSSStyleDeclaration.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index 52f1a998..dbeffeed 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -141,6 +141,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. + * This setter is called by Array.prototype.splice.call(). * * @param {number} len */ From 05f240d0cfec42a07e69869750c082df6d3304ec Mon Sep 17 00:00:00 2001 From: "asamuzaK (Kazz)" Date: Thu, 4 Dec 2025 08:39:27 +0900 Subject: [PATCH 29/29] Fix setProperty() logic --- lib/CSSStyleDeclaration.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/CSSStyleDeclaration.js b/lib/CSSStyleDeclaration.js index dbeffeed..4331d55f 100644 --- a/lib/CSSStyleDeclaration.js +++ b/lib/CSSStyleDeclaration.js @@ -205,8 +205,12 @@ class CSSStyleDeclaration { this._checkReadonly(prop); const value = prepareValue(val, this._global); if (value === "") { - this[prop] = ""; - this.removeProperty(prop); + // Implicitly change longhand properties and remove property itself. + if (shorthandProperties.has(prop)) { + this[prop] = ""; + } else { + this.removeProperty(prop); + } return; } if (priority) {