postcss-property-lookup
Version:
PostCSS plugin that allows referencing property values without a variable
107 lines (87 loc) • 2.32 kB
JavaScript
const t = require('tcomb');
const plugin = 'postcss-property-lookup';
const lookupPattern = /@\(?([a-z-]+)\)?\b/g;
const LogLevel = t.enums.of(['error', 'warn'], 'LogLevel');
const PluginOptions = t.struct(
{
logLevel: LogLevel,
},
'PluginOptions'
);
const defaultOptions = {
logLevel: 'warn',
};
function resolveLookup(rule, orig, prop, log, result) {
const resolvedValue = closest(rule, prop, log, result);
if (!resolvedValue) {
log(`Unable to find property ${orig} in ${rule.selector}`, rule, result);
}
return resolvedValue;
}
function eachDecl(container, callback) {
container.each((node) => {
if (node.type === 'decl') {
callback(node);
}
// Recurse through child declarations of a media rule
if (node.type === 'atrule') {
eachDecl(node, callback);
}
});
}
function closest(container, prop, log, result) {
if (!container) {
return '';
}
let resolvedValue;
eachDecl(container, (decl) => {
if (decl.prop === prop) {
resolvedValue = decl.value;
}
});
// Ignore a reference to itself
// e.g a {color: @color;}
if (resolvedValue && resolvedValue.replace('@', '') === prop) {
return '';
}
if (!resolvedValue) {
return closest(container.parent, prop, log, result);
}
if (resolvedValue.indexOf('@') === -1) {
return resolvedValue;
}
return resolvedValue.replace(
lookupPattern,
(orig, property) => resolveLookup(container, orig, property, log, result)
);
}
module.exports = function propertyLookup(options) {
const errorContext = {plugin};
options = new PluginOptions(Object.assign({}, defaultOptions, options));
const log = {
warn(message, rule, result) {
rule.warn(result, message);
},
error(message, rule) {
throw rule.error(message, errorContext);
},
}[options.logLevel];
if (!log) {
throw new Error(`Invalid logLevel: ${options.logLevel}`);
}
return {
postcssPlugin: plugin,
Rule(rule, {result}) {
eachDecl(rule, (decl) => {
if (decl.value.indexOf('@') === -1) {
return;
}
decl.value = decl.value.replace(
lookupPattern,
(orig, property) => resolveLookup(rule, orig, property, log, result)
);
});
},
};
};
module.exports.postcss = true;