Javascript regex- How to find PascalCase items inside of group

Viewed 65

I'm needing to convert something like this:

[[Click here|ThisIsALink]]

to:

[Click here](https://example.com/this-is-a-link)

I'm able to find the first [[Click here|ThisIsALink]] by using this regex /\[\[(.*?)\|(.*?)\]\]/g which I can replace to turn it into [Click here](https://example.com/ThisIsALink]), but how then can I take that last part and convert it to this-is-a-link? I've tried a few things but I seem to be finding all PascalCased occurrences and not just the occurrences that happen within the first found regex pattern. (I am converting .md files to .pdf, so it is a full page of content, not just these links).

Here's what I have so far

var exampleUrl = "https://example.com/";
var urlOne = /\[\[(.*?)\|(.*?)\]\]/g;

data.replace(urlOne, "[$1](" + exampleUrl + "$2)"));

This results in: [Click here](https://example.com/ThisIsALink)

2 Answers

You can use

const texts = ["Text [[Click here|ThisIsALink]] text...",
               "Text [[Click here|This-Is-A-Link]] text..."];
const exampleUrl = "https://example.com/";
const urlOne = /\[\[((?:(?!\[\[).)*?)\|(.*?)]]/g;
for (var text of texts) {
  console.log(
    text.replace(urlOne, (_,x,y) =>
                 `[${x}](${exampleUrl}${y.replace(/[^A-Za-z]+/g, '')
                                         .replace(/(?!^)[A-Z]/g, '-$&')
                                         .toLowerCase()})`) );
}

Regex #1 details:

  • \[\[ - [[ string
  • ((?:(?!\[\[).)*?) - Group 1: any char other than line break chars, zero or more times, as few as possible, that does not start [[ char sequence
  • \| - a | char
  • (.*?) - Group 2: any zero or more chars other than line break chars, as few as possible
  • ]] - a ]] string.

The second regex, (?!^)[A-Z], is applied to the Group 2 contents, and matches any uppercase ASCII letter that is not at the start of the string. The -$& replacement just appends - before the match.

The extra regex, .replace(/[^A-Za-z]+/g, ''), will remove all non-alpha chars from the Group 2 contents before inserting hyphens.

This approach does find and replace the by the OP mentioned pattern in a text of any given length regardless of how often the pattern is matched within this text.

It also uses a very straightforward regex for String.prototype.replace.

The regex does look like that ... /\[\[([^|]+)\|([^\]]+)\]\]/g ... and reads like this ...

  1. \[\[ ... match (literally) two open square brackets ...
  2. ([^|]+) ... capture every character sequence (the link text) that is not a pipe ...
  3. \| ... then match a pipe ...
  4. ([^\]]+) ... capture every character sequence (the very url) that is not a closing square bracket ...
  5. \]\] ... then match two closing square brackets.
  6. / ... /g ... flag regex as globally to be processed/applied pattern.

const sampleUrl = 'https://example.com/'
const sampleText = `Text [[Link A|ThisIsLinkA]] text. Text [[Link B|thisIsLinkB]] text.
Text [[Link C|This-Is-Link-C]] text.
Text [[Link D|ThisIsLinkD]] text. Text [[Link E|-this--Is--Link--E]] text.`;

function convertLinkMarkup(text, baseUrl) {
  function ensureKebabCase(str) {
    return str
      .replace((/([A-Z])/g), '-$1') // - prefix any latin uppercase char with '-'.
      .replace((/-+/g), '-')        // - replace any '-' sequence with a single '-'.
      .replace((/^-+/g), '')        // - trim any leading '-' sequence.
      .toLowerCase();               // - lower-case the result.
  }
  function createLinkMarkup(match, text, path) {
    return `[${ text }](${ baseUrl }${ ensureKebabCase(path) })`;
  }
  const regXUrl = (/\[\[([^|]+)\|([^\]]+)\]\]/g);

  return text.replace(regXUrl, createLinkMarkup);
}

console.log(
  sampleText,
  '\n\n... => ...\n\n',
  convertLinkMarkup(sampleText, sampleUrl)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

Related