Reputation: 22674
I have the following string:
@something {
color: red;
background: yellow;
}
I am trying to add heyaaa
before the closing bracket, for example..
@something {
color: red;
background: yellow;
heyaaa
}
I'm using gulp string replace to search a css file for @something
and add the needed heyaaa
string before the closing bracket.
I tried the following but does not work...
.pipe(replace('^(\@something\s*{(?:\n.*)*?\n)}', 'heyaaa'))
Other regular expressions work so I know there is nothing wrong with my setup.
Upvotes: 0
Views: 69
Reputation: 163352
You are capturing the part from the start until the last semicolon in a group and after that you are matching the closing curly brace. But to get that back in the replacement you have to refer to the capturing group. What you have matched will not be there because you are replacing what you have matched.
To fix your regex you could capture the last }
in a group ^(\@something\s*{(?:\n.*)*?\n)(})
and in the replacement refer to those groups.
const regex = /^(\@something\s*{(?:\n.*)*?\n)(})/gm;
const str = `@something {
color: red;
background: yellow;
}`;
const subst = `$1 heyaa\n$2`;
const result = str.replace(regex, subst);
console.log(result);
To take the start of the string into account and the indentation of the last line, you could match the first line, then repeat while not looking at a new line followed by a }
.
You could capture the whitespace characters at the start in a capturing group and refer to that in the replacement to match the indenting for heyaaa
:
^(@[^{]+{\s*(?:(?!\n})(\s+).*\n)*)(})
Pattern explanation
^
Start(
Capture group
@[^{]+{\s*
Match @
, 1+t imes not {
. Then match {
and 0+ times a whitespace char(?:
Non capturing group
(?!\n})(\s+).*\n
Assert what is on the right is not a newline followed by a }
. If that is the case, match whole line followed by a new line)*
Close group and repeat 0+ times)
Close capturing group(})
Capture Closing parenthesis$
EndIn the replacement you could use the 3 capturing groups:
$1$2heyaaa\n$3
Using the callback function, your code might look like:
.pipe(replace(/^(@[^{]+{\s*(?:(?!\n})(\s+).*\n)*)(})/, function(_,g1,g2,g3) {
return g1 + g2 + "heyaaa\n" + g3; }
)
)
const regex = /^(@[^{]+{\s*(?:(?!\n})(\s+).*\n)*)(})/gm;
const str = `@something {
color: red;
background: yellow;
}`;
const subst = `$1$2heyaaa\n$3`;
const result = str.replace(regex, subst);
console.log(result);
Upvotes: 1
Reputation: 37755
One way is to capture everything before last }
in a group and in callback function of replace use that captured group along with the value you want.
let str = `@something {
color: red;
background: yellow;
}`
let op = str.replace(/^(@something\s*[\w\W]+)}$/g,function(_,g1){
return g1 + ' heyya,\n}'
})
console.log(op)
Upvotes: 0
Reputation: 1906
The problem is in the second parameter of replace
function: your code replaces what's matched with heyaaa
instead of inserting heyaaa
at the place you want.
You can simply do what follows, referring to $1
(first matched group) in the second parameter (replacement):
const input = `@something {
color: red;
background: yellow;
}
@otherthing {
color: red;
background: yellow;
}
`
const regex = /^(\@something+\s*{(?:\n.*)*?\n)}/gm
const result0 = input.replace(regex, 'NEW TEXT TO BE INSERTED') // Original substitution
const result1 = input.replace(regex, '$1 NEW TEXT TO BE INSERTED\n}') // Fixed substitution
console.log(result0) // Original result
console.log(result1) // Fixed result
UPDATED: EXPLANATION
I added m
suffix for multi-line regex match and g
suffix for multiple matching (without it the reagexp is applied on the first match only).
If you need to add NEW TEXT TO BE INSERTED
at the end of each CSS class, you should change @something
into @[^\s]
in the regex (see following snippet).
const input = `@something {
color: red;
background: yellow;
}
@otherthing {
color: red;
background: yellow;
}
`
const regex = /^(\@[^\s]+\s*{(?:\n.*)*?\n)}/gm
const result = input.replace(regex, '$1 NEW TEXT TO BE INSERTED\n}') // Fixed substitution
console.log(result) // Fixed result
Upvotes: 1