How to avoid capturing groups if the captured match is empty?

Use \1 to refer to the separator captured in the first (\.|\/) group so we don't have to keep writing it over and over.

const text = `I navigated to news.google.com
I navigated to news/google/com
I navigated to dev.maps.yahoo.fr
I navigated to dev/maps/yahoo/fr`;

const re = /\w+(\.|\/)(\w+\1)?(google|yahoo)\1\w+/g;
console.log(text.replace(re, (url, separator) => `custom${separator}${url}`));

Here's an alternate solution given the new requirement described in the comments:

const text = `I navigated to news.google.com
I navigated to news/google/com
I navigated to dev.maps.yahoo.fr
I navigated to dev/maps/yahoo/fr`;

const re = /(news|dev)(\.|\/)(google|maps)\2(com|yahoo)(\2fr)?/g;

console.log(text.replace(re, (url, prefix, separator) => `custom${separator}${url}`));

Yet another alternate solution:

const text = `I navigated to news.google.com
I navigated to news/google/com
I navigated to dev.maps.yahoo.fr
I navigated to dev/maps/yahoo/fr`;

const re = /news(\.)google\.com|news(\/)google\/com|dev(\.)maps\.yahoo\.fr|dev(\/)maps\/yahoo\/fr/g;

console.log(text.replace(re, url => 'custom' + url.match(/\.|\//)[0] + url));