The Tech Platform

Oct 19, 20213 min

REMOVE INLINE CSS FROM HTML FILES

What does the script do?

1. The script recursively scans all html files in a directory
 
2. For each html file, it
 
a. scans for ‘style’ element recursively
 
b. replaces each style element with a css class ‘cx — <tagName>_<count>’
 
c. deletes the style element
 
3. All the created css classes are written to a file ‘generatedCSS.scss’

Steps to run the script

1. Copy package.json and RemoveInlineCSS.js file to a directory
 
2. In the directory, execute commands
 
a. npm install → This will install all the required dependencies
 
b. npm start → This will run the script
 
3. The script will prompt for a directory name <dirName> having HTML files to be processed
 
4. After successful script execution, existing html files will be updated and a new css file will be generated at location <dirName>/generatedCSS.scss

SCRIPTS

1. package.json

{
 
"name":"remove_inline_css",
 
"version":"1.0.0",
 
"description":"",
 
"scripts":{
 
"start":"node RemoveInlineCSS.js"
 
},
 
"author":"madhuri4011@gmail.com",
 
"license":"ISC",
 
"dependencies":{
 
"absurd":"⁰.3.9",
 
"fs":"0.0.1-security",
 
"glob":"⁷.1.4",
 
"inline-style-2-json":"¹.1.0",
 
"node-html-parser":"¹.1.16",
 
"readline":"¹.3.0"
 
}
 
}

2. RemoveInlineCSS.js

var HTMLParser = require('node-html-parser'); // HTML parser
 
var absurd = require("absurd"); // CSS preprocessor
 
var inline_style_2_json = require("inline-style-2-json");
 
//Converts CSS inline stlyes to JSON
 
var fs = require('fs'); // To access file system
 
var glob = require('glob');
 
//Used to find all HTML files in a drectory recursivelyconst readline = require('readline');
 
var count = 1;
 
var dirname = "";
 
var cssFile = "";
 
var api = absurd(cssFile);
 
var callBackCounter = 0;
 
// accept directory path from user
 
const rl = readline.createInterface({
 
input: process.stdin,
 
output: process.stdout
 
});
 
rl.question('Enter directory path? ', (path) => {
 
dirname = path;
 
cssFile = `${dirname}/generatedCSS.scss`;
 
console.log("read data: " + dirname);
 
readFiles(dirname, compileCSS);
 
rl.close();
 
});
 
var compileCSS = function (err, success) {
 
api.compile(function (err, css) {
 
console.log("Compiled CSS: " + css);
 
fs.writeFile(cssFile, css, function () {
 
console.log("CSS file written successfully");
 
});
 
});
 
};
 

 
function processFile(filename, content, cb) {
 
var global = HTMLParser.parse(content, {
 
style: true
 
});
 
if (global.childNodes.length > 0) {
 
extractInlineCSS(global, true, filename, function () {
 
cb();
 
});
 
}
 
}
 

 
function readFiles(dirname, cb) {
 
glob(dirname + "/**/*.html", null, function (er, files) {
 
files.forEach(function (file) {
 
console.log(file);
 
fs.readFile(file, 'utf-8', function (err, content) {
 
if (err) {
 
return;
 
}
 
processFile(file, content, function () {
 
callBackCounter++;
 
if (callBackCounter == files.length) {
 
cb();
 
}
 
});
 
});
 
});
 
});
 
}
 

 
function extractInlineCSS(parentNode, isRoot, filename, cb) {
 
for (let i = 0; i < parentNode.childNodes.length; i++) {
 
var childNode = parentNode.childNodes[i];
 
if (childNode.attributes != undefined && childNode.attributes.style != undefined) {
 
console.log("***" + childNode.attributes.style);
 
let styleJsonObj = inline_style_2_json(childNode.attributes.style);
 
let htmlClassName = "cx - " + childNode.tagName + "_" + count;
 
let cssClassName = ".cx - " + childNode.tagName + "_" + count++;
 
let style = {
 
[cssClassName]: styleJsonObj
 
};
 
api.add(style);
 
// if a given node already has a class attribute
 
if (childNode.attributes.class != undefined) {
 
let newClasses = childNode.attributes.class + " " + htmlClassName;
 
childNode.attributes.class = newClasses;
 
childNode.rawAttributes.class = newClasses;
 
}
 
// A given node doesn't have a class attribute
 
else {
 
childNode.attributes.class = htmlClassName;
 
childNode.rawAttributes.class = htmlClassName;
 
}
 
delete childNode.attributes.style;
 
var str = '';
 
let updatedAttributes = childNode.attributes;
 
for (let i = 0; i < Object.keys(updatedAttributes).length; i++) {
 
str += `${Object.keys(updatedAttributes)[i]}="${updatedAttributes[Object.keys(updatedAttributes)[i]]}" `;
 
}
 
childNode.rawAttrs = str;
 
// console.log(`class ` + childNode.attributes.class);
 
// console.log(`updated html: ` + parentNode.toString());
 
}
 
if (childNode.childNodes.length > 0) {
 
extractInlineCSS(childNode, false, filename, cb);
 
}
 
};
 
if (isRoot) {
 
fs.writeFile(filename, parentNode.toString(), function (err) {
 
if (!err) {
 
console.log(`${filename} has been updated successfully.`);
 
cb();
 
}
 
});
 
}
 
}

3. Sample Test HTML — TestFile.html

<!DOCTYPE html>
 
<html>
 
<body>
 

 
<p style="color:red;">I am red</p>
 

 
<div style="color:red;">
 
I am red again
 

 
<div style="color:blue;">
 
I am blue
 

 
<p style="color:yellow;">
 
I am yellow
 

 
</p>
 

 
</div>
 

 
<p style="font-size:50px;">I am big</p>
 

 
</div>
 
</body>
 
</html>
 

4. Output

TestFile.html

<html>
 
<body>
 

 
<p class="cx - p_1" >I am red</p>
 

 
<div class="cx - div_2" >
 
I am red again
 

 
<div class="cx - div_3" >
 
I am blue
 

 
<p class="cx - p_4" >
 
I am yellow
 

 
</p>
 

 
</div>
 

 
<p class="cx - p_5" >I am big</p>
 

 
</div>
 
</body>
 
</html>

GeneratedCSS.scss

.cx - p_1, .cx - div_2 {
 
color: red;
 
}
 
.cx - div_3 {
 
color: blue;
 
}
 
.cx - p_4 {
 
color: yellow;
 
}
 
.cx - p_5 {
 
font-size: 50px;
 
}

Limitations


 
The script strips-off any comments and <!DOCTYPE html> in html file

PS: The script was written around 2 years back hence we may need to upgrade the library versions and the script can be enhanced using ES6 async/await.

Source: Medium - Madhuri Pednekar

The Tech Platform

www.thetechplatform.com

    0