Skip to content

Instantly share code, notes, and snippets.

@erikyo
Last active May 24, 2023 18:03
Show Gist options
  • Save erikyo/074a567184a1924caf6f6f6353d461b2 to your computer and use it in GitHub Desktop.
Save erikyo/074a567184a1924caf6f6f6353d461b2 to your computer and use it in GitHub Desktop.
CSS file parser for removing duplicates among multiple css files

By using this script, you can easily remove duplicated CSS properties and prettify the resulting CSS files, making your stylesheets more organized and optimized.

Description:

This Node.js script helps parse multiple CSS files, remove duplicated CSS properties, and save the processed files with the "new-" prefix. The script utilizes the css package for parsing and manipulating CSS, as well as the prettier package for prettifying the resulting CSS output.

Key Features:

Accepts an array of CSS file names as input. Parses each CSS file using the css package, building an Abstract Syntax Tree (AST) representation. Removes duplicate CSS properties by comparing rules across all previous files. Utilizes the prettier package to format the processed CSS output. Saves the processed CSS files with the "new-" prefix in the same location as the source files.

Installation:

Ensure you have Node.js installed on your machine. Install the required packages by running the command npm install.

Usage:

  • Place the provided script file (duplicated-css.js) in your project directory.
  • Open the script file and modify the cssFiles array with the names of the CSS files you want to process.
  • Run the script using the command node duplicated-css.js.
  • The script will process the CSS files, remove duplicate properties, prettify the resulting CSS, and save the processed files with the "new-" prefix in the same location as the source files.

Examples:

Suppose you have three CSS files in your project directory: file1.css, file2.css, and file3.css. The contents of these files are as follows:

file1.css:

body {
  line-height: 1;
  font-family: Arial, Helvetica, sans-serif;
}

file2.css:

body {
  font-family: Arial, Helvetica, sans-serif;
}

file3.css:

body {
  line-height: 1;
  font-family: Arial, Helvetica, sans-serif;
}

After running the script using node duplicated-css.js, the processed files will be saved as follows:

new-file1.css:

body {
  line-height: 1;
  font-family: Arial, Helvetica, sans-serif;
}

new-file2.css:

body {
}

new-file3.css:

body {
}

The duplicate CSS properties are removed from file2.css and file3.css, resulting in empty rule blocks.

Feel free to customize the script based on your specific requirements or extend its functionality to suit your needs.

const fs = require('fs');
const path = require('path');
const css = require('css');
const prompts = require('prompts');
function removeDuplicates(cssFiles, numberOfFiles) {
const parsedASTs = []; // Array to store parsed ASTs
cssFiles.forEach(filename => {
const filePath = path.resolve(filename);
const fileContents = fs.readFileSync(filePath, 'utf-8');
const ast = css.parse(fileContents);
if (parsedASTs.length < numberOfFiles) {
parsedASTs.push(ast); // Store parsed AST for current file
} else {
// Loop through previous parsed ASTs to check for duplicates
for (let i = 0; i < numberOfFiles; i++) {
const previousAST = parsedASTs[i];
// Remove duplicate rules
ast.stylesheet.rules.forEach(rule => {
if (rule.type === 'rule') {
const selectors = rule.selectors.join(', ');
// Check if rule already exists in previous ASTs
previousAST.stylesheet.rules.forEach(prevRule => {
if (
prevRule.type === 'rule' &&
prevRule.selectors.join(', ') === selectors
) {
// Remove duplicate declarations
rule.declarations = rule.declarations.filter(declaration => {
return !prevRule.declarations.some(prevDeclaration => {
return (
prevDeclaration.property === declaration.property &&
prevDeclaration.value === declaration.value
);
});
});
}
});
}
});
}
}
const processedCSS = css.stringify(ast, { compress: false });
const newFilePath = path.join(path.dirname(filePath), `new-${path.basename(filePath)}`);
// Save processed CSS with "new-" prefix
fs.writeFileSync(newFilePath, processedCSS, 'utf-8');
});
}
// Get the list of CSS files in the current folder
const cssFiles = fs.readdirSync(__dirname).filter((filename) => filename.endsWith('.css'));
// Prepare the prompt options
const promptOptions = cssFiles.map((filename) => ({
title: filename,
value: filename,
}));
// Create a prompt asking the user to select CSS files
(async () => {
const response = await prompts([
{
type: 'multiselect',
name: 'selectedFiles',
message: 'Choose the CSS files to use:',
choices: promptOptions,
min: 1,
},
{
type: 'number',
name: 'numberOfFiles',
message: 'Enter the number of files to use as reference (against all other files):',
min: 1,
max: cssFiles.length,
initial: 1,
},
]);
// Remove duplicates from the selected files array
return removeDuplicates(response.selectedFiles, Number(response.numberOfFiles));
})();
body {
background: red;
line-height: 1;
font-family: Arial, Helvetica, sans-serif;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
span {
vertical-align: middle;
display: inline-block;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
section {
display: block;
}
p {
margin: 0;
}
q:before,
q:after {
content: none;
}
span {
vertical-align: middle;
}
body {
line-height: 1;
font-family: Arial, Helvetica, sans-serif;
background: blue;
}
section {
display: block;
}
p {
margin: 0;
}
q:before,
q:after {
content: none;
}
span {
vertical-align: middle;
}
.asdsdasd {
color: aliceblue;
font-size: 20px;
font-weight: bold;
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
.text {
color: aliceblue;
font-size: 20px;
font-weight: bold;
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
{
"name": "css-rules-extractor",
"description": "",
"author": "codekraft",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "node duplicated-css.js"
},
"license": "ISC",
"dependencies": {
"css": "^3.0.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment