diff --git a/README.md b/README.md index 143bc66..52363b6 100755 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +
⚠️ PostCSS Color Custom Properties was moved to @csstools/postcss-plugins. ⚠️
+Read the announcement
+ # PostCSS Custom Properties [PostCSS][postcss] [![NPM Version][npm-img]][npm-url] diff --git a/package.json b/package.json index 4b35c8d..f31b143 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "node": ">=12" }, "dependencies": { - "postcss-values-parser": "^6" + "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.3" @@ -51,7 +51,7 @@ "postcss": "8.3.6", "postcss-tape": "6.0.1", "pre-commit": "1.2.2", - "rollup": "2.56.3" + "rollup": "2.57.0" }, "eslintConfig": { "env": { diff --git a/src/index.d.ts b/src/index.d.ts index 59a286d..af24373 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -20,7 +20,7 @@ export interface PluginOptions { export interface Plugin { (options?: PluginOptions): { postcssPlugin: 'postcss-custom-properties', - prepare({ root: any }): ( + prepare({ root }: { root: any }): ( | { Declaration: (decl: any) => void; Once?: undefined; diff --git a/src/lib/get-custom-properties-from-imports.js b/src/lib/get-custom-properties-from-imports.js index c6aaf94..d0f2dd7 100644 --- a/src/lib/get-custom-properties-from-imports.js +++ b/src/lib/get-custom-properties-from-imports.js @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; -import { parse as postcssParse } from 'postcss'; -import { parse } from 'postcss-values-parser'; +import { parse } from 'postcss'; +import valuesParser from 'postcss-value-parser'; import getCustomPropertiesFromRoot from './get-custom-properties-from-root'; /* Get Custom Properties from CSS File @@ -9,7 +9,7 @@ import getCustomPropertiesFromRoot from './get-custom-properties-from-root'; async function getCustomPropertiesFromCSSFile(from) { const css = await readFile(from); - const root = postcssParse(css, { from }); + const root = parse(css, { from }); return getCustomPropertiesFromRoot(root, { preserve: true }); } @@ -25,7 +25,7 @@ function getCustomPropertiesFromObject(object) { ); for (const key in customProperties) { - customProperties[key] = parse(String(customProperties[key])).nodes; + customProperties[key] = valuesParser(String(customProperties[key])); } return customProperties; diff --git a/src/lib/get-custom-properties-from-root.js b/src/lib/get-custom-properties-from-root.js index b00ddeb..16dde0e 100644 --- a/src/lib/get-custom-properties-from-root.js +++ b/src/lib/get-custom-properties-from-root.js @@ -1,4 +1,4 @@ -import { parse } from 'postcss-values-parser'; +import valuesParser from 'postcss-value-parser'; import { isBlockIgnored } from './is-ignored'; // return custom selectors from the css root, conditionally removing them @@ -22,7 +22,7 @@ export default function getCustomPropertiesFromRoot(root, opts) { const { prop } = decl; // write the parsed value to the custom property - customPropertiesObject[prop] = parse(decl.value).nodes; + customPropertiesObject[prop] = valuesParser(decl.value); // conditionally remove the custom property declaration if (!opts.preserve) { diff --git a/src/lib/transform-properties.js b/src/lib/transform-properties.js index 4743410..e5651da 100644 --- a/src/lib/transform-properties.js +++ b/src/lib/transform-properties.js @@ -1,4 +1,4 @@ -import { parse } from 'postcss-values-parser'; +import valuesParser from 'postcss-value-parser'; import transformValueAST from './transform-value-ast'; import { isRuleIgnored } from './is-ignored'; @@ -6,16 +6,16 @@ import { isRuleIgnored } from './is-ignored'; export default (decl, customProperties, opts) => { if (isTransformableDecl(decl) && !isRuleIgnored(decl)) { const originalValue = decl.value; - const valueAST = parse(originalValue); - let value = String(transformValueAST(valueAST, customProperties)); + const valueAST = valuesParser(originalValue); + let value = transformValueAST(valueAST, customProperties); // protect against circular references const valueSet = new Set(); while (customPropertiesRegExp.test(value) && !valueSet.has(value)) { valueSet.add(value); - const parsedValueAST = parse(valueAST); - value = String(transformValueAST(parsedValueAST, customProperties)); + const parsedValueAST = valuesParser(value); + value = transformValueAST(parsedValueAST, customProperties); } // conditionally transform values that have changed diff --git a/src/lib/transform-value-ast.js b/src/lib/transform-value-ast.js index ad4b28c..0070bdb 100644 --- a/src/lib/transform-value-ast.js +++ b/src/lib/transform-value-ast.js @@ -1,47 +1,37 @@ export default function transformValueAST(root, customProperties) { if (root.nodes && root.nodes.length) { - root.nodes.slice().forEach(child => { + root.nodes.slice().forEach((child) => { if (isVarFunction(child)) { - // eslint-disable-next-line no-unused-vars - const [propertyNode, comma, ...fallbacks] = child.nodes; + const [propertyNode, ...fallbacks] = child.nodes.filter((node) => node.type !== 'div'); const { value: name } = propertyNode; + const index = root.nodes.indexOf(child); if (name in Object(customProperties)) { - // conditionally replace a known custom property - const nodes = asClonedArrayWithBeforeSpacing(customProperties[name], child.raws.before); - - /** - * https://github.com/postcss/postcss-custom-properties/issues/221 - * https://github.com/postcss/postcss-custom-properties/issues/218 - * - * replaceWith loses node.raws values, so we need to save it and restore - */ - const raws = nodes.map(node => ({...node.raws})); - - child.replaceWith(...nodes); - - nodes.forEach((node, index) => { - node.raws = raws[index]; - }); + // Direct match of a custom property to a parsed value + const nodes = customProperties[name].nodes; + // Re-transform nested properties without given one to avoid circular from keeping this forever retransformValueAST({ nodes }, customProperties, name); - } else if (fallbacks.length) { - // conditionally replace a custom property with a fallback - const index = root.nodes.indexOf(child); - if (index !== -1) { - root.nodes.splice(index, 1, ...asClonedArrayWithBeforeSpacing(fallbacks, child.raws.before)); + if (index > -1) { + root.nodes.splice(index, 1, ...nodes); + } + } else if (fallbacks.length) { + // No match, but fallback available + if (index > -1) { + root.nodes.splice(index, 1, ...fallbacks); } transformValueAST(root, customProperties); } } else { + // Transform child nodes of current child transformValueAST(child, customProperties); } }); } - return root; + return root.toString(); } // retransform the current ast without a custom property (to prevent recursion) @@ -57,35 +47,4 @@ function retransformValueAST(root, customProperties, withoutProperty) { const varRegExp = /^var$/i; // whether the node is a var() function -const isVarFunction = node => node.type === 'func' && varRegExp.test(node.name) && Object(node.nodes).length > 0; - -// return an array with its nodes cloned, preserving the raw -const asClonedArrayWithBeforeSpacing = (array, beforeSpacing) => { - const clonedArray = asClonedArray(array, null); - - if (clonedArray[0]) { - clonedArray[0].raws.before = beforeSpacing; - } - - return clonedArray; -}; - -// return an array with its nodes cloned -const asClonedArray = (array, parent) => array.map(node => asClonedNode(node, parent)); - -// return a cloned node -const asClonedNode = (node, parent) => { - const cloneNode = new node.constructor(node); - - for (const key in node) { - if (key === 'parent') { - cloneNode.parent = parent; - } else if (Object(node[key]).constructor === Array) { - cloneNode[key] = asClonedArray(node.nodes, cloneNode); - } else if (Object(node[key]).constructor === Object) { - cloneNode[key] = Object.assign({}, node[key]); - } - } - - return cloneNode; -}; +const isVarFunction = node => node.type === 'function' && varRegExp.test(node.value) && Object(node.nodes).length > 0; diff --git a/src/lib/write-custom-properties-to-exports.js b/src/lib/write-custom-properties-to-exports.js index e386452..649d702 100644 --- a/src/lib/write-custom-properties-to-exports.js +++ b/src/lib/write-custom-properties-to-exports.js @@ -130,9 +130,7 @@ export default function writeCustomPropertiesToExports(customProperties, destina const defaultCustomPropertiesToJSON = customProperties => { return Object.keys(customProperties).reduce((customPropertiesJSON, key) => { const valueNodes = customProperties[key]; - customPropertiesJSON[key] = valueNodes.map((propertyObject) => { - return propertyObject.toString(); - }).join(' '); + customPropertiesJSON[key] = valueNodes.toString(); return customPropertiesJSON; }, {}); diff --git a/test/export-properties.css b/test/export-properties.css index 2c70d9d..66aa2dc 100644 --- a/test/export-properties.css +++ b/test/export-properties.css @@ -10,6 +10,6 @@ --margin: 0 10px 20px 30px; --shadow-color: rgb(255,0,0); --shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); - --font-family: "Open Sans" , sans-serif; + --font-family: "Open Sans", sans-serif; --theme-color: #053; } diff --git a/test/export-properties.js b/test/export-properties.js index f898cd3..cc73182 100644 --- a/test/export-properties.js +++ b/test/export-properties.js @@ -11,7 +11,7 @@ module.exports = { '--margin': '0 10px 20px 30px', '--shadow-color': 'rgb(255,0,0)', '--shadow': '0 6px 14px 0 color(var(--shadow-color) a(.15))', - '--font-family': '"Open Sans" , sans-serif', + '--font-family': '"Open Sans", sans-serif', '--theme-color': '#053' } }; diff --git a/test/export-properties.json b/test/export-properties.json index dd55ef8..b04500c 100644 --- a/test/export-properties.json +++ b/test/export-properties.json @@ -11,7 +11,7 @@ "--margin": "0 10px 20px 30px", "--shadow-color": "rgb(255,0,0)", "--shadow": "0 6px 14px 0 color(var(--shadow-color) a(.15))", - "--font-family": "\"Open Sans\" , sans-serif", + "--font-family": "\"Open Sans\", sans-serif", "--theme-color": "#053" } } diff --git a/test/export-properties.mjs b/test/export-properties.mjs index 0168900..374ff7c 100644 --- a/test/export-properties.mjs +++ b/test/export-properties.mjs @@ -10,6 +10,6 @@ export const customProperties = { '--margin': '0 10px 20px 30px', '--shadow-color': 'rgb(255,0,0)', '--shadow': '0 6px 14px 0 color(var(--shadow-color) a(.15))', - '--font-family': '"Open Sans" , sans-serif', + '--font-family': '"Open Sans", sans-serif', '--theme-color': '#053' }; diff --git a/test/export-properties.scss b/test/export-properties.scss index 69303c3..e741500 100644 --- a/test/export-properties.scss +++ b/test/export-properties.scss @@ -9,5 +9,5 @@ $circular-2: var(--circular); $margin: 0 10px 20px 30px; $shadow-color: rgb(255,0,0); $shadow: 0 6px 14px 0 color(var(--shadow-color) a(.15)); -$font-family: "Open Sans" , sans-serif; +$font-family: "Open Sans", sans-serif; $theme-color: #053;