liferay-theme-tasks
Version:
A set of tasks for building and deploying Liferay Portal themes.
276 lines (217 loc) • 6.5 kB
JavaScript
/**
* SPDX-FileCopyrightText: © 2012 Dustin Diaz
* SPDX-License-Identifier: MIT
*/
const builder = require('css-stringify');
const fs = require('fs');
const parser = require('./css-parse');
const bg = require('./plugins/bg');
const fa = require('./plugins/fontawesome');
const yui3 = require('./plugins/yui3');
function quad(v, m) {
// 1px 2px 3px 4px => 1px 4px 3px 2px
if ((m = v.trim().split(/\s+(?=[^)]*(?:[(]|$))/)) && m.length === 4) {
return [m[0], m[3], m[2], m[1]].join(' ');
}
return v;
}
function quad_radius(v) {
if (v.startsWith('var')) {
return v;
}
var m = v.trim().split(/\s+/);
// 1px 2px 3px 4px => 1px 2px 4px 3px
// since border-radius: top-left top-right bottom-right bottom-left
// will be border-radius: top-right top-left bottom-left bottom-right
if (m && m.length === 4) {
return [m[1], m[0], m[3], m[2]].join(' ');
}
else if (m && m.length === 3) {
// super odd how this works
// 5px 10px 20px => 10px 5px 10px 20px
// yeah it's pretty dumb
return [m[1], m[0], m[1], m[2]].join(' ');
}
return v;
}
function direction(v) {
return v.match(/ltr/) ? 'rtl' : v.match(/rtl/) ? 'ltr' : v;
}
function rtltr(v) {
if (v.match(/left/)) {
return 'right';
}
if (v.match(/right/)) {
return 'left';
}
return v;
}
function bgPosition(v) {
if (v.match(/\bleft\b/)) {
v = v.replace(/\bleft\b/, 'right');
}
else if (v.match(/\bright\b/)) {
v = v.replace(/\bright\b/, 'left');
}
var m = v.trim().split(/\s+/);
if (m && m.length === 1 && v.match(/(\d+)([a-z]{2}|%)/)) {
v = 'right ' + v;
}
if (m && m.length === 2 && m[0].match(/\d+%/)) {
// 30% => 70% (100 - x)
v = 100 - parseInt(m[0], 10) + '% ' + m[1];
}
return v;
}
// Grabs the value of the translate property
const TRANSLATE_REGEX = /(?<=translatex?(?!y)\((?!0))\S*(?=,|\))/gim;
function translate(v) {
// translate(1px) => translate(calc(1px * -1))
return v.replace(TRANSLATE_REGEX, 'calc($& * -1)');
}
var propertyMap = {
'-moz-border-radius-bottomleft': '-moz-border-radius-bottomright',
'-moz-border-radius-bottomright': '-moz-border-radius-bottomleft',
'-moz-border-radius-topleft': '-moz-border-radius-topright',
'-moz-border-radius-topright': '-moz-border-radius-topleft',
'-webkit-border-bottom-left-radius': '-webkit-border-bottom-right-radius',
'-webkit-border-bottom-right-radius': '-webkit-border-bottom-left-radius',
'-webkit-border-top-left-radius': '-webkit-border-top-right-radius',
'-webkit-border-top-right-radius': '-webkit-border-top-left-radius',
'border-bottom-left-radius': 'border-bottom-right-radius',
'border-bottom-right-radius': 'border-bottom-left-radius',
'border-left': 'border-right',
'border-left-color': 'border-right-color',
'border-left-width': 'border-right-width',
'border-radius-bottomleft': 'border-radius-bottomright',
'border-radius-bottomright': 'border-radius-bottomleft',
'border-radius-topleft': 'border-radius-topright',
'border-radius-topright': 'border-radius-topleft',
'border-right': 'border-left',
'border-right-color': 'border-left-color',
'border-right-width': 'border-left-width',
'border-top-left-radius': 'border-top-right-radius',
'border-top-right-radius': 'border-top-left-radius',
'left': 'right',
'margin-left': 'margin-right',
'margin-right': 'margin-left',
'padding-left': 'padding-right',
'padding-right': 'padding-left',
'right': 'left',
};
var valueMap = {
'-moz-border-radius': quad_radius,
'-webkit-border-radius': quad_radius,
'background-position': bgPosition,
'border-color': quad,
'border-radius': quad_radius,
'border-style': quad,
'border-width': quad,
'clear': rtltr,
direction,
'float': rtltr,
'margin': quad,
'padding': quad,
'text-align': rtltr,
'transform': translate,
};
function processRule(rule, index, list) {
var prev = list[index - 1];
if (prev && prev.type === 'comment' && prev.comment.trim() === '@noflip') {
return;
}
if (rule.declarations) {
rule.declarations.forEach((declaration) => {
processDeclaration(declaration, rule);
});
}
else if (rule.rules) {
rule.rules.forEach(processRule);
}
}
function processDeclaration(declaration, rule) {
// Ignore comments in declarations
if (declaration.type !== 'declaration') {
return;
}
// RegEx for comments is taken from http://www.w3.org/TR/CSS21/grammar.html
var commentRegEx = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g;
var prop = declaration.property.replace(commentRegEx, ''); // remove comments
var val = declaration.value.replace(commentRegEx, '');
var important = /!important/;
var isImportant = val.match(important);
var asterisk = prop.match(/^(\*+)(.+)/, '');
if (asterisk) {
prop = asterisk[2];
asterisk = asterisk[1];
}
else {
asterisk = '';
}
prop = propertyMap[prop] || prop;
val = valueMap[prop] ? valueMap[prop](val, {decl: declaration, rule}) : val;
if (!val.match(important) && isImportant) {
val += '!important';
}
declaration.property = asterisk + prop;
declaration.value = val;
}
function r2(css, options) {
var ast;
if (!options) {
options = {compress: true};
}
ast = parser(css, {filename: options.filename});
ast.stylesheet.rules.forEach(processRule);
return builder(ast, options);
}
module.exports.exec = function (args) {
const read = args[0];
const out = args[1];
const options = {compress: args[2] !== '--no-compress'};
let data;
/*
/ If no read arg then read from stdin
/ allows for standard piping and reading in
/ ex: lessc file.less | r2 > file-rtl.css
/ ex: r2 < file.css
*/
if (!read) {
var buffer = '';
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', (chunk) => {
buffer += chunk;
});
process.stdin.on('end', () => {
if (buffer) {
// eslint-disable-next-line no-console
console.log(r2(buffer, options));
}
});
}
else {
/*
/ If reading from a file then print to stdout or out arg
/ To stdout: r2 styles.css
/ To file: r2 styles.cc styles-rtl.css
*/
data = fs.readFileSync(read, 'utf8');
if (out) {
// eslint-disable-next-line no-console
console.log('Swapping ' + read + ' to ' + out + '...');
fs.writeFileSync(out, r2(data, options), 'utf8');
}
else {
// eslint-disable-next-line no-console
console.log(r2(data, options));
}
}
};
module.exports.swap = function (css, options) {
return r2(css, options);
};
module.exports.valueMap = valueMap;
fa.plug(module.exports);
bg.plug(module.exports);
yui3.plug(module.exports);