postcss-css-variables
Advanced tools
Comparing version
@@ -1,3 +0,9 @@ | ||
# v0.17.0 - 2020-4-24 | ||
# v0.18.0 - 2021-05-11 | ||
- [breaking] Add basic postcss 8 support (older versions of PostCSS no longer compatible) | ||
- Thank you to [@delucis](https://github.com/delucis) for the [contribution](https://github.com/MadLittleMods/postcss-css-variables/pull/129) | ||
# v0.17.0 - 2020-04-24 | ||
- Expand variables in AtRule properties | ||
@@ -7,3 +13,3 @@ - Thank you to [@pvande](https://github.com/pvande) for the [contribution](https://github.com/MadLittleMods/postcss-css-variables/pull/104) | ||
# v0.16.0 - 2020-4-24 | ||
# v0.16.0 - 2020-04-24 | ||
@@ -13,3 +19,3 @@ - Add ability to pass callback function to `options.preserve` to determine whether to preserve declaration | ||
# v0.15.0 - 2020-4-24 | ||
# v0.15.0 - 2020-04-24 | ||
@@ -24,3 +30,3 @@ - Fix algorithm to find balanced `var()` pairs and nested parenthesis | ||
# v0.13.0 - 2019-6-17 | ||
# v0.13.0 - 2019-06-17 | ||
@@ -32,3 +38,3 @@ - Add `options.preserveAtRulesOrder` so media queries are outputted in the order they are defined (as expected) | ||
# v0.12.0 - 2019-2-21 | ||
# v0.12.0 - 2019-02-21 | ||
@@ -38,7 +44,7 @@ - Accept whitespace in `var( --var )` expression | ||
# v0.11.0 - 2018-10-9 | ||
# v0.11.0 - 2018-10-09 | ||
- Fix JS-defined variables using `isImportant`, https://github.com/MadLittleMods/postcss-css-variables/pull/87 | ||
# v0.10.0 - 2018-9-25 | ||
# v0.10.0 - 2018-09-25 | ||
@@ -48,3 +54,3 @@ - Cast `opts.variables` variable values to string | ||
# v0.9.0 - 2018-6-26 | ||
# v0.9.0 - 2018-06-26 | ||
@@ -54,3 +60,3 @@ - Adds `opts.preserveInjectedVariables`, which when set to `false`, removes the `:root { ... }` custom property declarations added via `opts.variables` | ||
# v0.8.1 - 2018-3-21 | ||
# v0.8.1 - 2018-03-21 | ||
@@ -60,3 +66,3 @@ - Log `undefined` variables (available in `result.warnings()`) | ||
# v0.8.0 - 2017-8-8 | ||
# v0.8.0 - 2017-08-08 | ||
@@ -66,3 +72,3 @@ - Remove PostCSS `moveTo`/`append` deprecation warnings, [#50](https://github.com/MadLittleMods/postcss-css-variables/issues/50) | ||
# v0.7.0 - 2017-3-12 | ||
# v0.7.0 - 2017-03-12 | ||
@@ -72,3 +78,3 @@ - Resolve `var` usage in fallbacks, [#37](https://github.com/MadLittleMods/postcss-css-variables/issues/37) | ||
# v0.6.0 - 2016-9-23 | ||
# v0.6.0 - 2016-09-23 | ||
@@ -80,3 +86,3 @@ - Update/refactor readme | ||
# v0.5.2 - 2016-8-24 | ||
# v0.5.2 - 2016-08-24 | ||
@@ -91,19 +97,19 @@ - Fix [#42](https://github.com/MadLittleMods/postcss-css-variables/issues/42) where `opts.preserve` was not working inside at-rules | ||
# v0.5.0 - 2015-9-12 | ||
# v0.5.0 - 2015-09-12 | ||
- Upgrade to PostCSS v5. Fix [#20](https://github.com/MadLittleMods/postcss-css-variables/issues/20) | ||
# v0.4.0 - 2015-7-2 | ||
# v0.4.0 - 2015-07-02 | ||
- Fix [#15](https://github.com/MadLittleMods/postcss-css-variables/issues/15) - Remove slowness from cloning the `root` with `node.clone().removeAll()`. Now using `./lib/shallow-clone-node.js` to avoid cloning children which will get removed right after. - Thank you to [@ddprrt](https://github.com/ddprrt) for bringing up the slowness issue in this article, [PostCSS misconceptions](https://medium.com/@ddprrt/postcss-misconceptions-faf5dc5038df). | ||
# v0.3.9 - 2015-6-29 | ||
# v0.3.9 - 2015-06-29 | ||
- Remove `opts` global leak. Fix [#13](https://github.com/MadLittleMods/postcss-css-variables/issues/13) | ||
# v0.3.8 - 2015-5-28 | ||
# v0.3.8 - 2015-05-28 | ||
- Add support for pseudo selectors `:hover` `:before` | ||
# v0.3.7 - 2015-5-27 | ||
# v0.3.7 - 2015-05-27 | ||
@@ -113,7 +119,7 @@ - Fix [#7](https://github.com/MadLittleMods/postcss-css-variables/issues/7): Support for child combinator | ||
# v0.3.6 - 2015-5-21 | ||
# v0.3.6 - 2015-05-21 | ||
- Fix [#6](https://github.com/MadLittleMods/postcss-css-variables/issues/6). Variable usage in comma separated selector to use proper scope | ||
# v0.3.5 - 2015-5-12 | ||
# v0.3.5 - 2015-05-12 | ||
@@ -123,11 +129,11 @@ - Big refactor of code to reduce cyclomatic complexity. Still needs work though. | ||
# v0.3.4 - 2015-5-12 | ||
# v0.3.4 - 2015-05-12 | ||
- Fix variable referencing another variable resolution when being changed by at-rule | ||
# v0.3.3 - 2015-5-11 | ||
# v0.3.3 - 2015-05-11 | ||
- Add support for last piece of combinator chain in selector resolution matching. - `.foo + .bar` can match variables declared in `.bar` | ||
# v0.3.1 - 2015-5-5 | ||
# v0.3.1 - 2015-05-05 | ||
@@ -137,11 +143,11 @@ - Large overhaul of code to make it more robust on proper scope resolution. | ||
# v0.2.3 - 2015-5-4 | ||
# v0.2.3 - 2015-05-04 | ||
- Add support for CSS4 descendant selector `>>` syntax | ||
# v0.2.2 - 2015-5-1 | ||
# v0.2.2 - 2015-05-01 | ||
- Automatically prefix any variables defined in `options.variables` with `--` (according to CSS custom property syntax). | ||
# v0.2.1 - 2015-4-30 | ||
# v0.2.1 - 2015-04-30 | ||
@@ -153,4 +159,4 @@ - Added support for descendant selector nesting instead of just physical space nesting | ||
# v0.1.0 - 2015-4-29 | ||
# v0.1.0 - 2015-04-29 | ||
- First release |
380
index.js
@@ -9,3 +9,2 @@ // PostCSS CSS Variables (postcss-css-variables) | ||
var postcss = require("postcss"); | ||
var extend = require("extend"); | ||
@@ -68,3 +67,3 @@ | ||
module.exports = postcss.plugin("postcss-css-variables", function(options) { | ||
module.exports = (options = {}) => { | ||
var opts = extend({}, defaults, options); | ||
@@ -74,222 +73,227 @@ | ||
return function(css, result) { | ||
// Transform CSS AST here | ||
return { | ||
postcssPlugin: 'postcss-css-variables', | ||
Once(css, { decl, result, rule }) { | ||
// Transform CSS AST here | ||
/* * / | ||
try { | ||
/* */ | ||
/* * / | ||
try { | ||
/* */ | ||
// List of nodes that if empty, will be removed | ||
// We use this because we don't want to modify the AST when we still need to reference these later on | ||
var nodesToRemoveAtEnd = []; | ||
// List of nodes that if empty, will be removed | ||
// We use this because we don't want to modify the AST when we still need to reference these later on | ||
var nodesToRemoveAtEnd = []; | ||
// Keep track of the injected from `opts.variables` to remove at the end | ||
// if user passes `opts.preserveInjectedVariables = false` | ||
var injectedDeclsToRemoveAtEnd = []; | ||
// Keep track of the injected from `opts.variables` to remove at the end | ||
// if user passes `opts.preserveInjectedVariables = false` | ||
var injectedDeclsToRemoveAtEnd = []; | ||
// Map of variable names to a list of declarations | ||
var map = {}; | ||
// Map of variable names to a list of declarations | ||
var map = {}; | ||
// Add the js defined variables `opts.variables` to the map | ||
map = extend( | ||
map, | ||
Object.keys(opts.variables).reduce(function( | ||
prevVariableMap, | ||
variableName | ||
) { | ||
var variableEntry = opts.variables[variableName]; | ||
// Automatically prefix any variable with `--` (CSS custom property syntax) if it doesn't have it already | ||
variableName = | ||
variableName.slice(0, 2) === "--" | ||
? variableName | ||
: "--" + variableName; | ||
var variableValue = (variableEntry || {}).value || variableEntry; | ||
var isImportant = (variableEntry || {}).isImportant || false; | ||
// Add the js defined variables `opts.variables` to the map | ||
map = extend( | ||
map, | ||
Object.keys(opts.variables).reduce(function( | ||
prevVariableMap, | ||
variableName | ||
) { | ||
var variableEntry = opts.variables[variableName]; | ||
// Automatically prefix any variable with `--` (CSS custom property syntax) if it doesn't have it already | ||
variableName = | ||
variableName.slice(0, 2) === "--" | ||
? variableName | ||
: "--" + variableName; | ||
var variableValue = (variableEntry || {}).value || variableEntry; | ||
var isImportant = (variableEntry || {}).isImportant || false; | ||
// Add a root node to the AST | ||
var variableRootRule = postcss.rule({ selector: ":root" }); | ||
css.root().prepend(variableRootRule); | ||
// Add the variable decl to the root node | ||
var varDecl = postcss.decl({ | ||
prop: variableName, | ||
value: variableValue, | ||
important: isImportant | ||
}); | ||
variableRootRule.append(varDecl); | ||
// Add a root node to the AST | ||
var variableRootRule = rule({ selector: ":root" }); | ||
css.root().prepend(variableRootRule); | ||
// Add the variable decl to the root node | ||
var varDecl = decl({ | ||
prop: variableName, | ||
value: variableValue, | ||
important: isImportant | ||
}); | ||
variableRootRule.append(varDecl); | ||
// Collect JS-injected variables for removal if `opts.preserveInjectedVariables = false` | ||
if (!opts.preserveInjectedVariables) { | ||
injectedDeclsToRemoveAtEnd.push(varDecl); | ||
} | ||
// Collect JS-injected variables for removal if `opts.preserveInjectedVariables = false` | ||
if (!opts.preserveInjectedVariables) { | ||
injectedDeclsToRemoveAtEnd.push(varDecl); | ||
} | ||
// Add the entry to the map | ||
prevVariableMap[variableName] = ( | ||
prevVariableMap[variableName] || [] | ||
).concat({ | ||
decl: varDecl, | ||
prop: variableName, | ||
calculatedInPlaceValue: variableValue, | ||
isImportant: isImportant, | ||
variablesUsed: [], | ||
parent: variableRootRule, | ||
isUnderAtRule: false | ||
// Add the entry to the map | ||
prevVariableMap[variableName] = ( | ||
prevVariableMap[variableName] || [] | ||
).concat({ | ||
decl: varDecl, | ||
prop: variableName, | ||
calculatedInPlaceValue: variableValue, | ||
isImportant: isImportant, | ||
variablesUsed: [], | ||
parent: variableRootRule, | ||
isUnderAtRule: false | ||
}); | ||
return prevVariableMap; | ||
}, | ||
{}) | ||
); | ||
// Chainable helper function to log any messages (warnings) | ||
var logResolveValueResult = function(valueResult) { | ||
// Log any warnings that might of popped up | ||
var warningList = [].concat(valueResult.warnings); | ||
warningList.forEach(function(warningArgs) { | ||
warningArgs = [].concat(warningArgs); | ||
result.warn.apply(result, warningArgs); | ||
}); | ||
return prevVariableMap; | ||
}, | ||
{}) | ||
); | ||
// Keep the chain going | ||
return valueResult; | ||
}; | ||
// Chainable helper function to log any messages (warnings) | ||
var logResolveValueResult = function(valueResult) { | ||
// Log any warnings that might of popped up | ||
var warningList = [].concat(valueResult.warnings); | ||
warningList.forEach(function(warningArgs) { | ||
warningArgs = [].concat(warningArgs); | ||
result.warn.apply(result, warningArgs); | ||
}); | ||
// Collect all of the variables defined | ||
// --------------------------------------------------------- | ||
// --------------------------------------------------------- | ||
//console.log('Collecting variables defined START'); | ||
eachCssVariableDeclaration(css, function(decl) { | ||
var declParentRule = decl.parent; | ||
// Keep the chain going | ||
return valueResult; | ||
}; | ||
var valueResults = logResolveValueResult(resolveValue(decl, map)); | ||
// Split out each selector piece into its own declaration for easier logic down the road | ||
decl.parent.selectors.forEach(function(selector) { | ||
// Create a detached clone | ||
var splitOutRule = shallowCloneNode(decl.parent); | ||
splitOutRule.selector = selector; | ||
splitOutRule.parent = decl.parent.parent; | ||
// Collect all of the variables defined | ||
// --------------------------------------------------------- | ||
// --------------------------------------------------------- | ||
//console.log('Collecting variables defined START'); | ||
eachCssVariableDeclaration(css, function(decl) { | ||
var declParentRule = decl.parent; | ||
var declClone = decl.clone(); | ||
splitOutRule.append(declClone); | ||
var valueResults = logResolveValueResult(resolveValue(decl, map)); | ||
// Split out each selector piece into its own declaration for easier logic down the road | ||
decl.parent.selectors.forEach(function(selector) { | ||
// Create a detached clone | ||
var splitOutRule = shallowCloneNode(decl.parent); | ||
splitOutRule.selector = selector; | ||
splitOutRule.parent = decl.parent.parent; | ||
var prop = decl.prop; | ||
map[prop] = (map[prop] || []).concat({ | ||
decl: declClone, | ||
prop: prop, | ||
calculatedInPlaceValue: valueResults.value, | ||
isImportant: decl.important || false, | ||
variablesUsed: valueResults.variablesUsed, | ||
parent: splitOutRule, | ||
// variables inside root or at-rules (eg. @media, @support) | ||
isUnderAtRule: splitOutRule.parent.type === "atrule" | ||
}); | ||
}); | ||
var declClone = decl.clone(); | ||
splitOutRule.append(declClone); | ||
let preserveDecl; | ||
if (typeof opts.preserve === "function") { | ||
preserveDecl = opts.preserve(decl); | ||
} else { | ||
preserveDecl = opts.preserve; | ||
} | ||
// Remove the variable declaration because they are pretty much useless after we resolve them | ||
if (!preserveDecl) { | ||
decl.remove(); | ||
} | ||
// Or we can also just show the computed value used for that variable | ||
else if (preserveDecl === "computed") { | ||
decl.value = valueResults.value; | ||
} | ||
// Otherwise just keep them as var declarations | ||
//else {} | ||
var prop = decl.prop; | ||
map[prop] = (map[prop] || []).concat({ | ||
decl: declClone, | ||
prop: prop, | ||
calculatedInPlaceValue: valueResults.value, | ||
isImportant: decl.important || false, | ||
variablesUsed: valueResults.variablesUsed, | ||
parent: splitOutRule, | ||
// variables inside root or at-rules (eg. @media, @support) | ||
isUnderAtRule: splitOutRule.parent.type === "atrule" | ||
}); | ||
// We add to the clean up list if we removed some variable declarations to make it become an empty rule | ||
// We clean up later on because we don't want to modify the AST when we still need to reference these later on | ||
if (declParentRule.nodes.length <= 0) { | ||
nodesToRemoveAtEnd.push(declParentRule); | ||
} | ||
}); | ||
//console.log('Collecting variables defined END'); | ||
let preserveDecl; | ||
if (typeof opts.preserve === "function") { | ||
preserveDecl = opts.preserve(decl); | ||
} else { | ||
preserveDecl = opts.preserve; | ||
} | ||
// Remove the variable declaration because they are pretty much useless after we resolve them | ||
if (!preserveDecl) { | ||
decl.remove(); | ||
} | ||
// Or we can also just show the computed value used for that variable | ||
else if (preserveDecl === "computed") { | ||
decl.value = valueResults.value; | ||
} | ||
// Otherwise just keep them as var declarations | ||
//else {} | ||
// Resolve variables everywhere | ||
// --------------------------------------------------------- | ||
// --------------------------------------------------------- | ||
// We add to the clean up list if we removed some variable declarations to make it become an empty rule | ||
// We clean up later on because we don't want to modify the AST when we still need to reference these later on | ||
if (declParentRule.nodes.length <= 0) { | ||
nodesToRemoveAtEnd.push(declParentRule); | ||
} | ||
}); | ||
//console.log('Collecting variables defined END'); | ||
// Collect all the rules that have declarations that use variables | ||
var rulesThatHaveDeclarationsWithVariablesList = []; | ||
css.walk(function(rule) { | ||
// We're only interested in Containers with children. | ||
if (rule.nodes === undefined) return; | ||
// Resolve variables everywhere | ||
// --------------------------------------------------------- | ||
// --------------------------------------------------------- | ||
var doesRuleUseVariables = rule.nodes.some(function(node) { | ||
if (node.type === "decl") { | ||
var decl = node; | ||
// If it uses variables | ||
// and is not a variable declarations that we may be preserving from earlier | ||
if ( | ||
resolveValue.RE_VAR_FUNC.test(decl.value) && | ||
!RE_VAR_PROP.test(decl.prop) | ||
) { | ||
return true; | ||
} | ||
} | ||
// Collect all the rules that have declarations that use variables | ||
var rulesThatHaveDeclarationsWithVariablesList = []; | ||
css.walk(function(rule) { | ||
// We're only interested in Containers with children. | ||
if (rule.nodes === undefined) return; | ||
return false; | ||
}); | ||
var doesRuleUseVariables = rule.nodes.some(function(node) { | ||
if (node.type === "decl") { | ||
var decl = node; | ||
// If it uses variables | ||
// and is not a variable declarations that we may be preserving from earlier | ||
if ( | ||
resolveValue.RE_VAR_FUNC.test(decl.value) && | ||
!RE_VAR_PROP.test(decl.prop) | ||
) { | ||
return true; | ||
} | ||
if (doesRuleUseVariables) { | ||
rulesThatHaveDeclarationsWithVariablesList.push(rule); | ||
} | ||
return false; | ||
}); | ||
if (doesRuleUseVariables) { | ||
rulesThatHaveDeclarationsWithVariablesList.push(rule); | ||
} | ||
}); | ||
rulesThatHaveDeclarationsWithVariablesList.forEach(function(rule) { | ||
var rulesToWorkOn = [].concat(rule); | ||
// Split out the rule into each comma separated selector piece | ||
// We only need to split if it's actually a Rule with multiple selectors (comma separated) | ||
if (rule.type === "rule" && rule.selectors.length > 1) { | ||
// Reverse the selectors so that we can cloneAfter in the same comma separated order | ||
rulesToWorkOn = rule.selectors.reverse().map(function(selector) { | ||
var ruleClone = rule.cloneAfter(); | ||
ruleClone.selector = selector; | ||
rulesThatHaveDeclarationsWithVariablesList.forEach(function(rule) { | ||
var rulesToWorkOn = [].concat(rule); | ||
// Split out the rule into each comma separated selector piece | ||
// We only need to split if it's actually a Rule with multiple selectors (comma separated) | ||
if (rule.type === "rule" && rule.selectors.length > 1) { | ||
// Reverse the selectors so that we can cloneAfter in the same comma separated order | ||
rulesToWorkOn = rule.selectors.reverse().map(function(selector) { | ||
var ruleClone = rule.cloneAfter(); | ||
ruleClone.selector = selector; | ||
return ruleClone; | ||
}); | ||
return ruleClone; | ||
}); | ||
rule.remove(); | ||
} | ||
rule.remove(); | ||
} | ||
// Resolve the declarations | ||
rulesToWorkOn.forEach(function(ruleToWorkOn) { | ||
ruleToWorkOn.nodes.slice(0).forEach(function(node) { | ||
if (node.type === "decl") { | ||
var decl = node; | ||
resolveDecl( | ||
decl, | ||
map, | ||
opts.preserve, | ||
opts.preserveAtRulesOrder, | ||
logResolveValueResult | ||
); | ||
} | ||
// Resolve the declarations | ||
rulesToWorkOn.forEach(function(ruleToWorkOn) { | ||
ruleToWorkOn.nodes.slice(0).forEach(function(node) { | ||
if (node.type === "decl") { | ||
var decl = node; | ||
resolveDecl( | ||
decl, | ||
map, | ||
opts.preserve, | ||
opts.preserveAtRulesOrder, | ||
logResolveValueResult | ||
); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); | ||
// Clean up any nodes we don't want anymore | ||
// We clean up at the end because we don't want to modify the AST when we still need to reference these later on | ||
nodesToRemoveAtEnd.forEach(cleanUpNode); | ||
// Clean up any nodes we don't want anymore | ||
// We clean up at the end because we don't want to modify the AST when we still need to reference these later on | ||
nodesToRemoveAtEnd.forEach(cleanUpNode); | ||
// Clean up JS-injected variables marked for removal | ||
injectedDeclsToRemoveAtEnd.forEach(function(injectedDecl) { | ||
injectedDecl.remove(); | ||
}); | ||
// Clean up JS-injected variables marked for removal | ||
injectedDeclsToRemoveAtEnd.forEach(function(injectedDecl) { | ||
injectedDecl.remove(); | ||
}); | ||
//console.log('map', map); | ||
//console.log('map', map); | ||
/* * / | ||
} | ||
catch(e) { | ||
//console.log('e', e.message); | ||
console.log('e', e.message, e.stack); | ||
} | ||
/* */ | ||
/* * / | ||
} | ||
catch(e) { | ||
//console.log('e', e.message); | ||
console.log('e', e.message, e.stack); | ||
} | ||
/* */ | ||
} | ||
}; | ||
}); | ||
}; | ||
module.exports.postcss = true; |
{ | ||
"name": "postcss-css-variables", | ||
"version": "0.17.0", | ||
"version": "0.18.0", | ||
"description": "PostCSS plugin to transform CSS Custom Properties(CSS variables) syntax into a static representation", | ||
@@ -19,4 +19,3 @@ "keywords": [ | ||
"escape-string-regexp": "^1.0.3", | ||
"extend": "^3.0.1", | ||
"postcss": "^6.0.8" | ||
"extend": "^3.0.1" | ||
}, | ||
@@ -31,5 +30,9 @@ "devDependencies": { | ||
"mocha": "^5.2.0", | ||
"postcss": "^8.2.6", | ||
"postcss-discard-comments": "^4.0.0", | ||
"postcss-normalize-whitespace": "^4.0.0" | ||
}, | ||
"peerDependencies": { | ||
"postcss": "^8.2.6" | ||
}, | ||
"scripts": { | ||
@@ -36,0 +39,0 @@ "test": "mocha", |
@@ -10,3 +10,3 @@ # PostCSS CSS Variables | ||
``` | ||
npm install postcss-css-variables --save-dev | ||
npm install postcss postcss-css-variables --save-dev | ||
``` | ||
@@ -266,3 +266,3 @@ | ||
There is another similar plugin available, [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), although it restricts itself much more than this plugin, preferring partial spec conformance. This plugin has the same capabilities but also adds imperfect feature support which stem from not being to know what the DOM will look like when you compile your CSS. We instead look at the explicit structure of your CSS selectors. | ||
There is another similar plugin available, [`postcss-custom-properties`](https://github.com/postcss/postcss-custom-properties), although it restricts itself much more than this plugin, preferring partial spec conformance. This plugin has the same capabilities but also adds imperfect feature support which stem from not being able to know what the DOM will look like when you compile your CSS. We instead look at the explicit structure of your CSS selectors. | ||
@@ -269,0 +269,0 @@ ### Interoperability and differences from `postcss-custom-properties` |
62345
1.24%925
0.33%10
11.11%+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed