3

I'm trying to replace all class names in a CSS file. I am using JavaScript/Node.js.

My current solution is this: /\.[a-z][a-z0-9-_]*/g. At the end of the file is a comment that references the source map file: /*# sourceMappingURL=style.css.map */. Now the file extensions of that URL also get replaced.

Given the following input:

.text-center { text-align: center; }
table.simple{background:#fff;}.bg-white{background:#fff;}
/*# sourceMappingURL=style.css.map */
/*
Example comment file.css
*/

the result is:

.a { text-align: center; }
table.a{background:#fff;}.a{background:#fff;}
/*# sourceMappingURL=style.a.a */
/*
Example comment file.a
*/

what I want is:

.a { text-align: center; }
table.a{background:#fff;}.a{background:#fff;}
/*# sourceMappingURL=style.css.map */
/*
Example comment file.css
*/

How do I have to change my RegEx so it does only match class names, that are outside of comments (// comments are not relevant here)?

mbrandau
  • 544
  • 3
  • 14

4 Answers4

2

Try this regex:

\.[^{\s]*(?=\s*{)

and replace each match with .a

Click for Demo

Explanation:

  • \. - matches a .
  • [^{\n]* - matches 0+ occurrences of any character that is neither a white-space nor {
  • (?=\s*{) - positive lookahead to make sure that the current position is followed by 0+ whitespaces followed by {.

Now, the above regex would fail if we have comments of the format

/*# sourceMappingURL=style.css.map {*/

i.e, { present after css.map inside a comment. So, to handle such a case, you can use the following regex(almost similar to the previous regex. Just one more negative lookahead to the regex)

\.[^{\s]*(?=\s*{)(?!(?:(?!\/\*)[\s\S])*\*\/)

Click for Demo

Explanation:

  • \.[^{\s]*(?=\s*{) - similar to the previous regex
  • (?!(?:(?!\/\*)[\s\S])*\*\/) - Negative lookahead to make sure that the current position(position right after the .classname) is not present within a comment of the form /*...*/
    • (?!.... - negative lookahead
    • (?:(?!\/\*)[\s\S])* - tempered greedy token to match 0+ occurrences of any character greedily, which does not begin with the sequence /*
    • \*\/ - matches */
Gurmanjot Singh
  • 10,224
  • 2
  • 19
  • 43
1

Both of these approaches work in different cases like classes between ids or multiple classes in selection:

.class-a, #id-a, .class-b:hover {}

First approach

In this first approach you can match both comment section and CSS classes then replace class names while they are matched and leave comments intact:

\/\*[\s\S]*?\*\/|(\.[a-z_-][\w-]*)(?=[^{}]*{)
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 Match comments    Match and capture classes

Breakdown:

\/\*[\s\S]*?\*\/    # Match a comment block
|                   # Or
(                   # Start of capturing group #1
    \.[a-z_-][\w-]* # Match a CSS class name
)                   # End of CG #1
(?=                 # Start of a positive lookahead
    [^{}]*{         # Class should be followed by a `{` (may not be immediately)
)                   # End of lookahead

JS code:

var str = `.text-center { text-align: center; }
table.simple{background:#fff;}.bg-white{background:#fff;}
/*# sourceMappingURL=style.css.map */
/*
Example comment file.css
*/`

console.log(str.replace(/\/\*[\s\S]*?\*\/|(\.[a-z_-][\w-]*)(?=[^{}]*{[^{}]*})/g,
    function($0, $1) {
        return $1 ? '.a' : $0; // If a class is matched ...
    }
));

Second approach

If you don't think in comments such occurrences will exist:

/* style.css {css file} */

you can omit matching comments in first approach:

\.[a-z_-][\w-]*(?=[^{}]*{[^{}]*})
                         ^^^^^^^ 
             Added for a more strict selection

RegEx live demo

JS code:

var str = `.text-center { text-align: center; }
table.simple{background:#fff;}.bg-white{background:#fff;}
/*# sourceMappingURL=style.css.map */
/*
Example comment file.css
*/`

console.log(str.replace(/\.[a-z_-][\w-]*(?=[^{}]*{[^{}]*})/g, '.a'));
Community
  • 1
  • 1
revo
  • 47,783
  • 14
  • 74
  • 117
0

I've got it to work with the following RegEx in pcre flavor

/(\/\*)?(?(1)(.*\*\/)|(\.[a-z][a-z0-9-_]*))/g

I'm using a conditional statement. Whenever it detects a start of a comment, it does not look for a class name but for the end of the comment. However, this does not work for comments on multiple lines. The class names are the third capturing group. So to replace class names, only touch matches that contain the capturing group 3.

I didn't notice I was using pcre instead of javascript on regex101.com and ended up with a syntax error in my JS code. I'm now working on finding a solution that works for JS.

Edit:

I now ported it to JS:

/(?=\/\*)|(\.[a-z][a-z0-9-_]*)(?!.*\*\/)/g

I still have to find a way to get it to work with multiple lines.

mbrandau
  • 544
  • 3
  • 14
-1

With the following regex you should be able to extract all class-names from your css file, which are not in comment. It´s important that the class name start with a dot (.)

Regex:

/^.[a-z][a-z0-9-_].*?[\s]/mg

enter image description here

YanetP1988
  • 1,346
  • 3
  • 18
  • 43
Paul
  • 1
  • 1