top of page
Writer's pictureThe Tech Platform

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

0 comments

Comments


bottom of page