Update: Added @babel/plugin-transform-named-capturing-groups-regex per @Jack Misteli suggestion to look at Babel.
Update:: Using Babel to transpile the code, I then tested in a CSB forked from @Jack Misteli's. The key here is that when I try to return groups, it's null

I'm trying to understand what it is about Typescript (my config, the language, etc.) that's breaking this regular expression.
Specifically, it appears to have to do with the named capture groups that I was using because when I remove them, it works.
Details:
Original TS file:
/**
*
* @param {string} markdownLink A link in the form of [title](link description)
* @returns {object} An object with three keys: title, link, description
*/
export function parseLink(markdownLink: string) {
const pattern: RegExp = new RegExp(
/^(\[(?<title>[^\]]*)?\]\((?<link>[A-Za-z0-9\:\/\.\- ]+)(?<description>\"(.+)\")?\))/
)
const match = markdownLink.match(pattern)
const groups = match?.groups
return groups
}
This transpiled to (updated after adding the babel plugin):
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parseLink = parseLink;
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _wrapNativeSuper2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapNativeSuper"));
function _wrapRegExp(re, groups) { _wrapRegExp = function _wrapRegExp(re, groups) { return new BabelRegExp(re, undefined, groups); }; var _RegExp = (0, _wrapNativeSuper2["default"])(RegExp); var _super = RegExp.prototype; var _groups = new WeakMap(); function BabelRegExp(re, flags, groups) { var _this = _RegExp.call(this, re, flags); _groups.set(_this, groups || _groups.get(re)); return _this; } (0, _inherits2["default"])(BabelRegExp, _RegExp); BabelRegExp.prototype.exec = function (str) { var result = _super.exec.call(this, str); if (result) result.groups = buildGroups(result, this); return result; }; BabelRegExp.prototype[Symbol.replace] = function (str, substitution) { if (typeof substitution === "string") { var groups = _groups.get(this); return _super[Symbol.replace].call(this, str, substitution.replace(/\$<([^>]+)>/g, function (_, name) { return "$" + groups[name]; })); } else if (typeof substitution === "function") { var _this = this; return _super[Symbol.replace].call(this, str, function () { var args = []; args.push.apply(args, arguments); if ((0, _typeof2["default"])(args[args.length - 1]) !== "object") { args.push(buildGroups(args, _this)); } return substitution.apply(this, args); }); } else { return _super[Symbol.replace].call(this, str, substitution); } }; function buildGroups(result, re) { var g = _groups.get(re); return Object.keys(g).reduce(function (groups, name) { groups[name] = result[g[name]]; return groups; }, Object.create(null)); } return _wrapRegExp.apply(this, arguments); }
/**
*
* @param {string} markdownLink A link in the form of [title](link description)
* @returns {object} An object with three keys: title, link, description
*/
function parseLink(markdownLink) {
var pattern = new RegExp( /*#__PURE__*/_wrapRegExp(/^(\[([\0-\\\^-\uFFFF]*)?\]\(([ \.-:A-Za-z]+)("(.+)")?\))/, {
title: 2,
link: 3,
description: 4
}));
var match = markdownLink.match(pattern);
var groups = match === null || match === void 0 ? void 0 : match.groups;
return groups;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ...fQ==
My tsconfig is:
{
"compilerOptions": {
/* Basic Options */
"target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
"module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
"sourceMap": true /* Generates corresponding '.map' file. */,
"outDir": "dist" /* Redirect output structure to the directory. */,
"composite": true /* Enable project compilation */,
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,
/* Module Resolution Options */
"allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
/* Advanced Options */
"skipLibCheck": true /* Skip type checking of declaration files. */,
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */,
"resolveJsonModule": true
},
"include": ["./src"]
}
My .babelrc:
{
"presets": ["@babel/preset-env", "@babel/preset-typescript"],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-named-capturing-groups-regex"
]
}
It's worth noting that the two patterns:
- The one I wrote:
^(\[(?<title>[^\]]*)?\]\((?<link>[A-Za-z0-9\:\/\.\- ]+)(?<description>\"(.+)\")?\)) - And the transpiled one
/^(\[([\0-\\\^-\uFFFF]*)?\]\(([ \.-:A-Za-z]+)("(.+)")?\))/
Do not do the same thing (see the links for tests).
If I remove the named capture groups, it does seem to work, though I'm now struggling to understand why I have a duplicate full capture group.
In the above image, you can see I'm comparing the native JS version with the imported, transpiled version (from the dist folder).
The debugger on the left shows the value of parsed, which is the variable assigned to the return of the transpiled version.
So - my questions:
- Is there something I need to do to enable named capture groups in TS? Update It's not related to TS, but Babel. The answer is to use @babel/plugin-transform-named-capturing-groups-regex
- Is there something I should know to understand why I'm getting a duplicate group for the transpiled version?
- Is there a better way to do this? :)
- What am I doing wrong with configuring the named capture groups plugin that it still doesn't return a match when it's present?
