CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
Script to find unused CSS styles

 

First page Previous page 1 2 Next page Last page  View All


mohan
User

Jul 17, 2016, 10:48 PM

Post #1 of 28 (10861 views)
Script to find unused CSS styles Can't Post

Hi Guys,

I have a set of CSS files and HTML files in my local drive and need to find certain styles in css (lets say "margin-top" is the style I want to find in the CSS).

If this is found then I want to find the class in which "margin-top" is declared. Lets say "margin" class has this style.

Then the class "margin" should be checked across all the HTML files to check whether it has been used at least once or not.

Then the result should be shown.

is there any script that you can run on your local machine to find this out? Any help is welcomed.

(This post was edited by mohan on Jul 28, 2016, 11:24 PM)


Laurent_R
Veteran / Moderator

Jul 17, 2016, 11:24 PM

Post #2 of 28 (10859 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

You basically need two nested loops. Read all you CSS file names and search each of them in the HTML files, or, the other way around, look for code for CSS files into your HTML files, mark any CSS file used and at the end unused CSS files will not be marked.

You can save quite a bit of runtime if you store your CSS file names in a hash aat the beginning, because you can replace the inner loop by a hash lookup, which is far faster if the number of CSS files is large.

Have you tried to write some code? If so, please show the code.


mohan
User

Jul 17, 2016, 11:34 PM

Post #3 of 28 (10857 views)
Re: [Laurent_R] Script to find unused CSS styles [In reply to] Can't Post

I have a code that will search the HTML and CSS for certain styles


Code
use strict;  
use warnings;
use Cwd;

my @filedefs =
(
#################To CSS for Float/Inline-block/Non Amazon media queries/####################################
{
extension => 'CSS',
preaction => sub { $_[0]->{count} = 0 },
testaction => sub { $_[0]->{count}++ },
postaction => sub { print "$_[0]->{label} !! $_[0]->{count} Instance found\n" },
operations =>
[
{
label => 'Float Value',
test => sub { $_[0] =~ /float\s*(:|=)\s*[^%]+$/ },
},
{
label => 'Display inline block',
test => sub { $_[0] =~ /inline-block/ },
},
{
label => 'Non-Amazon media query',
test => sub { $_[0] =~ /not amzn-mobi/ },
},
],
},

#########################################################################################################################




##############To check the OPF file for SVG image refernce####################
{
extension => 'opf',
operations =>
[


{
label => 'toc',
test => sub { $_[0] =~ /(svg|\Qname="svg" content="true"\E)/ },
testaction => sub { $_[0]->{done} = 1 },
postaction => sub { print +( $_[0]->{done} ) ? 'SVG reference is found' : 'SVG reference is not found', "\n" },
},
],
} ,
################################################################################################

#################To check xhtml files for SVG tags############################################
{
extension => 'xhtml',
operations =>
[

{
label => 'svg',
test => sub { $_[0] =~ /(svg|\Qname="svg" content="true"\E)/ },
testaction => sub { $_[0]->{done} = 1 },
postaction => sub { print +( $_[0]->{done} ) ? 'SVG tag is found' : 'SVG tag is not found', "\n" },

},
]
},
################################################################################################


#################To check html files for SVG tags############################################
{
extension => 'html',
operations =>
[

{
label => 'svg',
test => sub { $_[0] =~ /(svg|\Qname="svg" content="true"\E)/ },
testaction => sub { $_[0]->{done} = 1 },
postaction => sub { print +( $_[0]->{done} ) ? 'SVG tag is found' : 'SVG is not found', "\n" },

},
]
},
################################################################################################
);


my $dirpath = getcwd;

for my $filedef ( @filedefs )
{
my @filepaths = glob( "$dirpath/*.$filedef->{extension}" );

print "\nNO $filedef->{extension} FILES FOUND\n" unless @filepaths;

for my $filepath ( @filepaths )
{
print "\n>>> $filepath <<<\n";

do { $_->{done} = 0; $_->{preaction} // $filedef->{preaction} // sub { } }->( $_ ) for ( @{$filedef->{operations}} );

open my $filehandle, '<', $filepath or die "could not open '$filepath': $!";

while ( my $line = <$filehandle> )
{
# note the below two lines could be handled more efficiently in a testaction, which
# returns a true or false value to indicate if last etc, because its unnecessary to check
# for every line.
my @operations = grep { not $_->{done} } @{$filedef->{operations}};
last unless @operations;

chomp $line;

do { $_->{test} // sub { 0 } }->( $line ) and do { $_->{testaction} // $filedef->{testaction} // sub { } }->( $_, $line ) for ( @operations )
}

close $filehandle;

do { $_->{postaction} // $filedef->{postaction} // sub { } }->( $_ ) for ( @{$filedef->{operations}} );
}
}

END
{
print "\nPress enter to exit\n";
<>;
}


It would be great if I get a sample code so that I can modify and build it up as per my need.


mohan
User

Jul 28, 2016, 8:33 PM

Post #4 of 28 (10783 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Any help?


Laurent_R
Veteran / Moderator

Jul 28, 2016, 11:17 PM

Post #5 of 28 (10781 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Hm, two points.

I fail to see the relation between what you asked for in your original post and the code you posted. They are at best very remotely connected.

My second point is that if you're a beginner, then you most probably did not write the code you posted, and you probably also don't understand it.

So my question is: do you need a program that does what you asked for in the OP, or do you need to update the program you posted so that it does what you are looking for? The answer to this question would lead to entirely different solution.


mohan
User

Jul 28, 2016, 11:24 PM

Post #6 of 28 (10777 views)
Re: [Laurent_R] Script to find unused CSS styles [In reply to] Can't Post

yes you are absolutely correct.

I have edited the OP in a more clear way.


Zhris
Enthusiast

Jul 30, 2016, 7:03 PM

Post #7 of 28 (10745 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Hi,

I helped you write that code in this thread. In this case you want to add a specific test that tests if a css property is used. You could certainly figure out a way to implement this test in that code, but thats a separate problem.

I suggest you use a set of established modules to take care of parsing the css, converting selectors to xpaths, then testing if a property exists. I have had a play around and came up with the following standalone demo designed to process a single set of html and corresponding css:


Code
use strict; 
use warnings;
use CSS;
use HTML::Selector::XPath 'selector_to_xpath';
use HTML::TreeBuilder::XPath;
use Data::Dumper;

my $properties = [ 'margin' ];

my ( $chunk_html, $chunk_css ) = chunks( );
my $tree = HTML::TreeBuilder::XPath->new_from_content( $chunk_html );
my $css = CSS->new( ); $css->read_string( $chunk_css );

my $ref = [ ];

for my $style ( @{$css->{styles}} )
{
for my $property ( grep { $style->get_property_by_name( $_ ) } @$properties )
{
for my $selector ( map { $_->{name} } @{$style->{selectors}} )
{
my $xpath = selector_to_xpath( $selector );
my $exists = $tree->exists( $xpath );

# $ref->{$selector} = $exists;
push @$ref,
{
property => $property,
selector => $selector,
xpath => $xpath,
exists => $exists,
};
}
}
}

print Dumper $ref;

sub chunks
{
local $/ = '__DATA__';

my $chunks = [ ];

while ( my $chunk = <DATA> )
{
chomp $chunk;

#open my $handle, '<', \$chunk;

push @$chunks, $chunk;
}

return @$chunks;
}

__DATA__

<html>
<head>
<title></title>
</head>
<body>
<div class="three"></div>
</body>
</html>

__DATA__

.one
{
margin: 0px;
}

.two
{
padding: 0px;
}

.three, .four
{
margin: 0px;
}


Output:


Code
$VAR1 = [ 
{
'exists' => 0,
'xpath' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' one \')]',
'property' => 'margin',
'selector' => '.one'
},
{
'selector' => '.three',
'exists' => 1,
'xpath' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' three \')]',
'property' => 'margin'
},
{
'selector' => '.four',
'xpath' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' four \')]',
'exists' => 0,
'property' => 'margin'
}
];


If this is something you can work from, have a play around and let us know if you have any issues.

Chris


(This post was edited by Zhris on Jul 30, 2016, 9:36 PM)


mohan
User

Jul 31, 2016, 8:25 PM

Post #8 of 28 (10707 views)
Re: [Zhris] Script to find unused CSS styles [In reply to] Can't Post

I just ran the below code for testing:


Code
use strict;  
use warnings;
use CSS;
use HTML::Selector::XPath 'selector_to_xpath';
use HTML::TreeBuilder::XPath;
use Data::Dumper;

my $properties = [ 'margin' ];

my ( $chunk_html, $chunk_css ) = chunks( );
my $tree = HTML::TreeBuilder::XPath->new_from_content( $chunk_html );
my $css = CSS->new( ); $css->read_string( $chunk_css );

my $ref = [ ];

for my $style ( @{$css->{styles}} )
{
for my $property ( grep { $style->get_property_by_name( $_ ) } @$properties )
{
for my $selector ( map { $_->{name} } @{$style->{selectors}} )
{
my $xpath = selector_to_xpath( $selector );
my $exists = $tree->exists( $xpath );

# $ref->{$selector} = $exists;
push @$ref,
{
property => $property,
selector => $selector,
xpath => $xpath,
exists => $exists,
};
}
}
}

print Dumper $ref;

sub chunks
{
local $/ = '__DATA__';

my $chunks = [ ];

while ( my $chunk = <DATA> )
{
chomp $chunk;

#open my $handle, '<', \$chunk;

push @$chunks, $chunk;
}

return @$chunks;
}

__DATA__

<html>
<head>
<title></title>
</head>
<body>
<div class="three"></div>
</body>
</html>

__DATA__

.one
{
margin: 0px;
}

.two
{
padding: 0px;
}

.three, .four
{
margin: 0px;
}

$VAR1 = [
{
'exists' => 0,
'xpath' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' one \')]',
'property' => 'margin',
'selector' => '.one'
},
{
'selector' => '.three',
'exists' => 1,
'xpath' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' three \')]',
'property' => 'margin'
},
{
'selector' => '.four',
'xpath' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' four \')]',
'exists' => 0,
'property' => 'margin'
}
];


But got the error message :


Code
Can't locate HTML/Selector/XPath.pm in @INC (you may need to install the HTML::S 
elector::XPath module) (@INC contains: C:/Perl64/site/lib C:/Perl64/lib .) at C:
\Users\mohad\Desktop\Check\test.pl line 4.
BEGIN failed--compilation aborted at C:\Users\mohad\Desktop\Check\test.pl line 4
.


Looks like I need to install the HTML module from http://search.cpan.org/~miyagawa/HTML-Selector-XPath-0.02/lib/HTML/Selector/XPath.pm.

how do i do this?


Laurent_R
Veteran / Moderator

Jul 31, 2016, 11:16 PM

Post #9 of 28 (10703 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Run the cpan or cpanm command.

http://www.cpan.org/modules/INSTALL.html


mohan
User

Jul 31, 2016, 11:33 PM

Post #10 of 28 (10699 views)
Re: [Laurent_R] Script to find unused CSS styles [In reply to] Can't Post

Thanks! I just learned how to install modules !

I got this message after I re-ran the prgm :


Code
Invalid or unexpected property '              'exists' => 0,              'xpath 
' => '//*[contains(concat(\' \', normalize-space(@class), \' \'), \' one \')]',
'property' => 'margin', 'selector' => '.one'
' in style '$VAR1 = [' at C:/Perl64/site/lib/CSS.pm line 76.



Zhris
Enthusiast

Aug 1, 2016, 12:55 AM

Post #11 of 28 (10695 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Remove the $VAR1 data structure from your code, that is the resultant output of the script.


mohan
User

Aug 1, 2016, 8:34 PM

Post #12 of 28 (10654 views)
Re: [Zhris] Script to find unused CSS styles [In reply to] Can't Post

What does the OP says? looks complicated


mohan
User

Aug 1, 2016, 8:36 PM

Post #13 of 28 (10653 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Ohhh I get it...it's just showing the class which has the style margin..correct?


mohan
User

Aug 1, 2016, 8:43 PM

Post #14 of 28 (10652 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

The code takes the data from

Code
   local $/ = '__DATA__';



I tried to change the "DATA" value and it didnt work.

How to make this run for the current directory from where it is run?

I have a folder where I have this script. So when I run this it should run for the entire folder (including the sub folders)


(This post was edited by mohan on Aug 1, 2016, 8:44 PM)


Laurent_R
Veteran / Moderator

Aug 2, 2016, 3:29 AM

Post #15 of 28 (10643 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

This is just an example, in which the __DATA__ sections represent two different files, an HTML file and a CSS file. In your real case, you need to open these files and read their contents into the appropriate variables.


mohan
User

Aug 2, 2016, 8:20 PM

Post #16 of 28 (10619 views)
Re: [Laurent_R] Script to find unused CSS styles [In reply to] Can't Post

I guess we can use the below code to search the entire directory for html and css

Code
my @filepaths = glob( "$dirpath/*.$filedef->{extension}" );



Laurent_R
Veteran / Moderator

Aug 2, 2016, 11:24 PM

Post #17 of 28 (10610 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Yes, probably (at least, the syntax seems correct, so if your variables are set correctly, it should work), but then, you still have to open the files and read them.


mohan
User

Aug 4, 2016, 7:57 PM

Post #18 of 28 (10566 views)
Re: [Laurent_R] Script to find unused CSS styles [In reply to] Can't Post

How do I do that? I'm lost here.


Laurent_R
Veteran / Moderator

Aug 4, 2016, 11:20 PM

Post #19 of 28 (10562 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

See the documentation on the open function:
http://perldoc.perl.org/functions/open.html


Zhris
Enthusiast

Aug 5, 2016, 8:31 AM

Post #20 of 28 (10539 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

The demo I provided main purpose was to show you the three HTML/CSS modules working together, roughly in the manner you need. Accomplishing your actual task will take more than just switching input sources. When I get a moment I'll write a more concise demo, though there are a few unknowns from your original description that make it difficult to know exactly what you need.

Chris


Zhris
Enthusiast

Aug 5, 2016, 1:18 PM

Post #21 of 28 (10531 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

Here it is. Please see attached for zip file containing script and test html / css files.

- It has been designed with efficiency in mind. I ensure it only parses each file once, and selectors are not converted to xpaths if the selector has already been seen. But I decided against holding a tree for every html document in memory before iterating over selectors.

- CSS provides a read_file method but 1) the documentation is wrong, multiple css files should be provided as an array reference, 2) the object doesn't associate selectors with files.

- I have tried to keep the output data as informative as possible, it contains anything I thought could be relevant. As a result it is quite intricate. Importantly, if key xpath value is undefined then HTML::Selector::XPath wasn't able to convert the selector, and if key html doesn't exist then the selector doesn't exist in any of the html documents. You should simplify the output data as per your actual needs.


Code
use strict; 
use warnings;
use CSS;
use HTML::Selector::XPath 'selector_to_xpath';
use HTML::TreeBuilder::XPath;
use Data::Dumper;

# input
my $input =
{
filepaths_css => [ glob( "*.css" ) ],
filepaths_html => [ glob( "*.html" ) ],
properties => [ qw/margin width/ ],
};

# output
my $output = { };

# iterate over each css file
for my $filepath_css ( @{$input->{filepaths_css}} )
{
# create new css object and populate it
my $css = CSS->new; $css->read_file( $filepath_css );

# iterate over each css block
for my $style ( @{$css->{styles}} )
{
# create list of properties that match those we want
my $properties = [ grep { $style->get_property_by_name( $_ ) } @{$input->{properties}} ];

# next iteration if list of properties that match is empty
next unless @$properties;

# iterate over each selector
for my $selector ( map { $_->{name} } @{$style->{selectors}} )
{
# if we have not seen this selector before, populate output with its xpath
unless ( exists $output->{$selector} )
{
$output->{$selector}->{xpath} = eval { selector_to_xpath( $selector ) };
}

# populate output with property and css file counts
$output->{$selector}->{properties}->{$_}++ for @$properties;
$output->{$selector}->{css}->{$filepath_css}++;
}
}
}

# iterate over each html file
for my $filepath_html ( @{$input->{filepaths_html}} )
{
# create new tree object and populate it
my $tree = HTML::TreeBuilder::XPath->new_from_file( $filepath_html );

# iterate over each selector
for my $selector ( grep { defined $output->{$_}->{xpath} } keys %$output )
{
# populate output with html file counts
my $nodes = $tree->findnodes( $output->{$selector}->{xpath} );
$output->{$selector}->{html}->{$filepath_html} += @$nodes if @$nodes;
}
}

# output
local $, = "\n";
local $\ = "\n";
print Dumper $output;
print grep { defined $output->{$_}->{xpath} and not exists $output->{$_}->{html} } keys %$output;


Chris


(This post was edited by Zhris on Aug 8, 2016, 8:43 AM)
Attachments: cssfind.zip (1.66 KB)


mohan
User

Aug 7, 2016, 8:14 PM

Post #22 of 28 (10473 views)
Re: [Zhris] Script to find unused CSS styles [In reply to] Can't Post

I'm getting this error message when I run it on my system:


Code
Invalid or unexpected style data ' "book" ' at C:/Perl64/site/lib/CSS.pm line 76 
.



Zhris
Enthusiast

Aug 7, 2016, 8:49 PM

Post #23 of 28 (10470 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

One of your CSS files is syntaxically invalid and the parser doesn't know how to handle it. Your options are:

- manually correct the mistake in the CSS file.
- use a different parser such as CSS::Parse::Heavy ( see documentation for CSS ), it may be more forgiving.
- override CSS::Parse::Lite's parse_string method to skip the iteration instead of croaking, though you run the risk of masking a cascade of problems.

Chris


mohan
User

Aug 7, 2016, 9:12 PM

Post #24 of 28 (10468 views)
Re: [Zhris] Script to find unused CSS styles [In reply to] Can't Post

Yes, one of the CSS had a invalid style and removing it fixed the issue.

I have a few questions:

1. What does xpath mean in the OP?
2. The OP look complicated and takes some time to read through it. Can it be simplified like:

->name of the CSS being analyzed
-> Property being checked
->Class that has the property
-> Whether that class as been used in the HTML.

3. Where to edit so that we can search for multiple properties?


Zhris
Enthusiast

Aug 7, 2016, 9:41 PM

Post #25 of 28 (10466 views)
Re: [mohan] Script to find unused CSS styles [In reply to] Can't Post

1) http://www.w3schools.com/xsl/xpath_intro.asp. Xpaths are used to navigate xml type documents. An xpath represents a path to a single or set of elements. Its much like a CSS selector, but has support for other complexities, In your case, we are converting selectors to xpaths so that we can navigate the html document and check if the element(s) exist. xpath is in the output data structure because we generate them in the first iteration group ( css ), then actually use them in the second iteration group ( html ) as this is most efficient. Feel free to delete it afterwards.

2) Yes, as mentioned in my previous post, you should simplify the data structure to suit your needs. The information you request is already part of the current data structure, but perhaps not laid our how you expect. Also, be careful with terminology, don't mix selector with class. If you really are only interested in selectors that lead to classes ( direct or indirect ), you can use CSS::Selector::Parser, and check that the last array of the last array has a class atrribute. Currently the code has no knowledge of specific types of element.

3) In the $input hash, you can modify the list of properties, currently there are two, margin and width.

Chris


(This post was edited by Zhris on Aug 8, 2016, 8:44 AM)

First page Previous page 1 2 Next page Last page  View All
 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives