@zohodesk/client_build_tool
Version:
A CLI tool to build web applications and client libraries
362 lines (302 loc) • 12.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _postcss = _interopRequireDefault(require("postcss"));
var _fs = _interopRequireDefault(require("fs"));
var _checkIsPatternsMatchFilename = require("../../loaderConfigs/checkIsPatternsMatchFilename");
var _ErrorHandler = require("./ErrorHandler");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// import { RawSource } from 'webpack-sources';
const ignoreVals = ['--zd_size', '--zd_font_size', '--size', '--size_'];
let variablesRead = {};
const supportedProps = ['font-size', 'margin', 'margin-top', 'margin-bottom', 'margin-left', 'margin-right', 'padding', 'padding-top', 'padding-bottom', 'padding-left', 'padding-right', '^top', '^right', '^bottom', '^left', '^width', 'min-width', 'max-width', '^height', 'min-height', 'max-height', 'text-indent', 'clip', 'flex-basis', 'row-gap', 'gap', 'column-gap', 'flex']; // const avoidProps = [];
// -- is issue IO --
/*
issues eg :
issues = ['--zd_size', '--zd_font_size', '--size', '--size_'];
input :
--zd_size
output :
true
comment :
do not execute when --zd_size comes as prop
*/
function isIgnoreValuePresent(ignoreVals, prop) {
let present = false;
ignoreVals.forEach(issue => {
if (prop && prop.includes(issue)) {
present = true;
}
});
return present;
} // -- to convert the hyphen values to values --
/*
input :
var(--zdt_uploadlist_default_width)
output :
--zdt_uploadlist_default_width
comment :
to make the variable object using the output as key and decl.prop such as font-size as value
*/
function extractVariableName(val) {
return val.replace(/calc\((.+)\)/gi, '$1').replace(/var\((.+)\)/gi, '$1').replace('-1', '').replace('*', '').replace('\n', '').trim();
}
function variableConvertor(rootOriginal, variables, settingsObject) {
rootOriginal.walkRules(rule => {
rule.nodes.forEach((decl, index) => {
const prevNode = rule.nodes[index - 1];
const currentNode = rule.nodes[index];
if (decl.prop && decl.prop.includes('--')) {
if (prevNode && prevNode.type === 'comment' && prevNode.text.toLowerCase() === 'variable:ignore') {
return;
}
if (isIgnoreValuePresent(ignoreVals, decl.prop)) {
return;
}
if (settingsObject[variables[decl.prop]]) {
/* if there is no value for property, set it to default so that undefined doesn't get called as key */
if (!variables[decl.prop]) {
// eslint-disable-next-line no-param-reassign
variables[decl.prop] = 'default';
}
const pxReplacement = settingsObject[variables[decl.prop]].replacements.px;
const valArr = decl.value.split(' '); // single values are considered in the above array and converted below
valArr.forEach((value, index) => {
if (value.includes('px')) {
const num = value.replace('px', '');
valArr[index] = pxReplacement.replace('$$', num);
}
});
currentNode.value = valArr.join(' ');
}
}
});
});
return rootOriginal;
}
function createFolderIfNonExistent(path) {
if (!_fs.default.existsSync(path)) {
_fs.default.mkdirSync(path, {
recursive: true
});
}
}
function createFileIfNonExistent(path, content) {
if (_fs.default.existsSync(path)) {
_fs.default.writeFileSync(path, content, 'utf-8');
}
}
class VariableConversionCollector {
constructor(options = {}) {
this.configFile = options.configFile;
this.patterns = options.patterns;
this.initializeFiles();
} // eslint-disable-next-line class-methods-use-this
initializeFiles() {
createFolderIfNonExistent('./.cli/logs/');
createFolderIfNonExistent('./.cli/config/variables/');
createFolderIfNonExistent('./.cli/config/selectorWeight/');
createFileIfNonExistent('./.cli/logs/unassignedVariables.log', '{}');
createFileIfNonExistent('./.cli/logs/css_error.log', '{}');
}
apply(compiler) {
const {
patterns
} = this;
const variables = {};
const unassigned = {}; // console.log(windowsModification([this.filename])[0]);
const rawdata = _fs.default.readFileSync(this.configFile);
const data = JSON.parse(rawdata);
const {
settings: settingsObject,
errorLog: errorLogStatus,
errorInConsole: errorConsoleStatus,
errorsAllowed,
strictMode
} = data; // If theres is no setting for default prop in settingsObject, set one.
if (!settingsObject.default) {
settingsObject.default = {
allowed: ['px', 'em', 'fit-content', 'auto', '%', 'inherit', '-moz-fit-content', 'vh', '0', 'initial', 'vw'],
replacements: {
px: 'var(--zd_size$$)'
},
range: {
start: -99999,
end: 99999
}
};
}
/*
purpose of tap : to create a variable object such as:
{
--zdt_uploadlist_default_width : width,
--zdt_uploadlist_default_height : height,
}
which will help in the conversion further
*/
if (_fs.default.existsSync('./.cli/config/variables/variableMapping.json')) {
variablesRead = JSON.parse(_fs.default.readFileSync('./.cli/config/variables/variableMapping.json', 'utf-8'));
Object.keys(variablesRead.changes).forEach(key => {
variables[key] = variablesRead.changes[key];
});
}
compiler.hooks.compilation.tap('VariableConversionCollector', compilation => {
compilation.hooks.optimizeModules.tap('VariableConversionCollector', modulesIterable => {
const modules = Array.from(modulesIterable);
const mods = modules.filter(x => x.type.includes('css'));
mods.forEach(module => {
const rootOriginal = _postcss.default.parse(module.content);
const filename = module.issuer.resource;
if (!filename.includes('node_modules')) {
rootOriginal.walkRules(rule => {
rule.walkDecls(decl => {
decl.value.split(' ').forEach(val => {
if (val && val.includes('--') && !new RegExp(ignoreVals.join('|'), 'gi').test(val) && decl.prop) {
const extractedValue = extractVariableName(val);
if (!variables[extractedValue]) {
variables[extractedValue] = decl.prop;
} else if (new RegExp(supportedProps.join('|'), 'gi').test(decl.prop)) {
// console.log(
// `${extractedValue} : ${variables[extractedValue]} already exists please check!`
// );
if (errorsAllowed.MULTIPLE_OCCURANCES) {
const errObj = {
decl,
type: 'MULTIPLE_OCCURANCES',
filename,
message: `${extractedValue} : ${variables[extractedValue]} already exists please check!`
};
_ErrorHandler.errHandler.errorTable.push(errObj);
_ErrorHandler.errHandler.errorFunction(errObj);
}
} // console.log(decl.prop);
} else if (/^--/gi.test(decl.prop) && val.trim() !== '' && !variables[decl.prop]) {
if (!Object.keys(variablesRead.ignore).includes(decl.prop)) {
unassigned[decl.prop] = variables[decl.prop];
}
}
});
});
});
/*
current value example:
{
--zdt_uploadlist_default_width : --zd_upload_width,
--zd_upload_width : width
}
expected value :
{
--zdt_uploadlist_default_width : width,
--zd_upload_width : width
}
conversion is done in the while loop below
*/
Object.keys(variables).forEach(key => {
while (variables[variables[key]]) {
variables[key] = variables[variables[key]];
}
});
}
});
}); // -- conversion for the root using variableConvertor --
/*
input :
.a{
padding : 20px
}
output :
// on settingObject file :
{ padding : { replacements : { px : 'zd_size$$' }}}
.a{
padding : zd_size20;
}
*/
compilation.hooks.optimizeModules.tap('VariableConversionCollector', modules => {
const mods = Array.from(modules).filter(x => x.type.includes('css'));
mods.forEach(module => {
const filename = module.issuer.resource;
/*
input :
filename : 'D:/MyWork/..../desk_client_app/supportapp/src/components/Avatar/Avatar.module.css,
patterns.cssVariableReplacement:
// include src folder, include deskapp folder, exclude node modules
[
"src",
"deskapp",
"!node_modules"
]
output :
true or false
*/
if ((0, _checkIsPatternsMatchFilename.checkIsPatternsMatchFilename)(patterns, filename) === false) {
return;
}
const rootOriginal = _postcss.default.parse(module.content); // eslint-disable-next-line no-param-reassign
module.content = variableConvertor(rootOriginal, variables, settingsObject).toString();
});
});
});
/*
purpose of tap : to display the errors encountered so far
input :
all css files
output :
all errors that are present in the errTable arr
*/
compiler.hooks.afterEmit.tap('error-display', () => {
const {
errors,
errorTable: errTable
} = _ErrorHandler.errHandler;
if (Object.keys(unassigned).length > 0 && strictMode) {
console.log();
console.log(unassigned);
console.log();
let str = '{\n';
Object.keys(unassigned).forEach(key => {
str += `"${key}" : "${unassigned[key]}",\n`;
});
str += '}';
_fs.default.writeFileSync('./.cli/logs/unassignedVariables.log', str, 'utf-8');
throw new Error('^^^ Variables above have not been assigned! ^^^');
}
const avlTypes = new Set([]); // eslint-disable-next-line array-callback-return, consistent-return
const srtArr = errTable.sort((a, b) => {
avlTypes.add(a.type);
avlTypes.add(b.type);
if (a.type < b.type) {
return -1;
}
});
if (errorConsoleStatus) {
const errorHandler = new _ErrorHandler.ErrorHandler();
avlTypes.forEach(type => {
console.log('---------------------------------------------------------------------------------------------------------------------------');
console.log(`Error Type : ${type}`);
console.log('---------------------------------------------------------------------------------------------------------------------------');
srtArr.forEach(err => {
if (err.decl.prop && err.decl.value && err.type === type) {
errorHandler.printError(err);
}
});
console.log('---------------------------------------------------------------------------------------------------------------------------');
});
}
if (errorLogStatus) {
_fs.default.writeFileSync('./.cli/logs/css_error.log', '');
console.log('writing to logFile...');
if (errors.length > 0) {
errors.forEach((err, index) => {
if (errTable[index].decl.prop && errTable[index].decl.value) {
_fs.default.appendFileSync('./.cli/logs/css_error.log', err);
}
});
}
}
});
}
}
var _default = VariableConversionCollector;
exports.default = _default;