I'm working on a JSON to CSS converter in NodeJS, which takes a .json file and generates a .css file with its utility classes from it.
.json example
}
"breakpoints-map": {
"default": "0",
"sm": "only screen and (min-width: 508px)",
"md": "only screen and (min-width: 768px)"
},
"bg-repeat-map": {
"bg-repeat": "repeat",
"bg-repeat-y": "repeat-y",
"bg-no-repeat": "no-repeat",
"bg-repeat-x": "repeat-x"
},
"bg-position-map": {
"bg-pos-top": "top",
"bg-pos-right-top": "top right",
"bg-pos-right-center": "right",
"bg-pos-right-bottom": "right bottom",
"bg-pos-bottom": "bottom",
"bg-pos-left-bottom": "left bottom",
"bg-pos-left": "left",
"bg-pos-left-top": "left top",
"bg-pos-center": "center center"
},
"bg-attachment-map": {
"bg-att-scroll": "scroll",
"bg-att-fixed": "fixed"
},
"border-style-map": {
"border-dotted": "dotted",
"border-dashed": "dashed",
"border-solid": "solid",
"border-double": "double"
}
}
.js code
module.exports = (fileName) => {
let cssWriter = fs.createWriteStream('style.css', {
flags: 'a'
});
fs.readFile(fileName, (error, data) => {
if (error) {
functions.logIt("The file cannot be found or is unreadable.", error);
} else {
try {
const dataJson = JSON.parse(data);
const breakpointMap = dataJson["breakpoints-map"]
delete dataJson["breakpoints-map"];
Object.entries(breakpointMap).forEach(([breakpointKey, breakpointValue]) => {
if (functions.isDefault(breakpointKey) == false) {
cssWriter.write("@media " + breakpointValue + " {\n");
}
Object.entries(dataJson).forEach(([mapKey, mapValues]) => {
let breakpoint = (functions.isDefault(breakpointKey) == true ? "" : breakpointKey + "\\:");
let property = functions.getProperty(mapKey);
Object.entries(mapValues).forEach(([classKey, classValue]) => {
cssWriter.write("." + breakpoint + classKey + "{ " + property + ": " + classValue + "}\n");
})
})
if (functions.isDefault(breakpointKey) == false) {
cssWriter.write("}\n");
}
})
} catch (error) {
functions.logIt("The file could not be parsed to JSON.", error);
}
}
cssWriter.end();
});
};
The isDefault
function just checks whether the given parameter is equal to "default", in order to not put a media query around it.
The getProperty
function just links the right CSS property depending on the map name (width-map => width, bg-repeat-map => background-position).
-
\$\begingroup\$ Welcome to Code Review. Read the tour if you haven't. \$\endgroup\$Gloweye– Gloweye2019年10月18日 15:05:41 +00:00Commented Oct 18, 2019 at 15:05
1 Answer 1
The main problem I see is that it's not obvious what the output is because there's too much auxiliary code that effectively obfuscates the logic.
- Offload some of the code into a function.
- Use template strings.
If the data amount isn't in multi-megabyte range I would write a single string to improve readability:
const dataJson = JSON.parse(data);
const breakpointMap = dataJson['breakpoints-map'];
delete dataJson['breakpoints-map'];
const entryToCss = ([mapKey, mapValues], breakpoint) => {
const property = functions.getProperty(mapKey);
return Object.entries(mapValues)
.map(([k, v]) => `.${breakpoint}${k}{ ${property}: ${v}}\n`)
.join('');
};
cssWriter.write(
Object.entries(breakpointMap).map(([bpKey, bpVal]) => {
const breakpoint = functions.isDefault(bpKey) ? `${bpKey}\\:` : '';
return `${
breakpoint ? `@media ${bpVal} {\n` : ''
}${
Object.entries(dataJson)
.map(entry => entryToCss(entry, breakpoint))
.join('')
}${
breakpoint ? '}\n' : ''
}`;
}).join('')
);