Skip to content

Commit

Permalink
Avoid calling regexpu for duplicated named groups when not necessary
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolo-ribaudo committed Aug 24, 2022
1 parent 306d6da commit 0e93aff
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 21 deletions.
23 changes: 5 additions & 18 deletions packages/babel-helper-create-regexp-features-plugin/src/index.ts
Expand Up @@ -95,18 +95,18 @@ export function createRegExpFeaturePlugin({
const features = file.get(featuresKey);
const runtime = file.get(runtimeKey) ?? true;

const regexpuOptions = generateRegexpuOptions(features);
if (canSkipRegexpu(node, regexpuOptions)) return;
const regexpuOptions = generateRegexpuOptions(node.pattern, features);
if (canSkipRegexpu(node, regexpuOptions)) {
return;
}

const namedCaptureGroups: Record<string, number | number[]> = {
__proto__: null,
};
let hasDuplicateNamedCaptureGroups = false;
if (regexpuOptions.namedGroups === "transform") {
regexpuOptions.onNamedGroup = (name, index) => {
const prev = namedCaptureGroups[name];
if (typeof prev === "number") {
hasDuplicateNamedCaptureGroups = true;
namedCaptureGroups[name] = [prev, index];
} else if (Array.isArray(prev)) {
prev.push(index);
Expand All @@ -116,20 +116,7 @@ export function createRegExpFeaturePlugin({
};
}

let pattern = rewritePattern(node.pattern, node.flags, regexpuOptions);
if (
!hasDuplicateNamedCaptureGroups &&
hasFeature(features, FEATURES.duplicateNamedCaptureGroups) &&
!hasFeature(features, FEATURES.namedCaptureGroups)
) {
// If we only transformed named groups because we want to transform duplicates
// but there are no duplicates, re-transform the regexp without transforming
// named groups.
regexpuOptions.namedGroups = false;
if (canSkipRegexpu(node, regexpuOptions)) return;
pattern = rewritePattern(node.pattern, node.flags, regexpuOptions);
}
node.pattern = pattern;
node.pattern = rewritePattern(node.pattern, node.flags, regexpuOptions);

if (
regexpuOptions.namedGroups === "transform" &&
Expand Down
23 changes: 20 additions & 3 deletions packages/babel-helper-create-regexp-features-plugin/src/util.ts
Expand Up @@ -3,7 +3,10 @@ import { FEATURES, hasFeature } from "./features";

import type { RegexpuOptions } from "regexpu-core";

export function generateRegexpuOptions(toTransform: number): RegexpuOptions {
export function generateRegexpuOptions(
pattern: string,
toTransform: number,
): RegexpuOptions {
type Experimental = 1;

const feat = <Stability extends 0 | 1 = 0>(
Expand All @@ -13,15 +16,29 @@ export function generateRegexpuOptions(toTransform: number): RegexpuOptions {
return hasFeature(toTransform, FEATURES[name]) ? ok : false;
};

const featDuplicateNamedGroups = (): "transform" | false => {
if (!feat("duplicateNamedCaptureGroups")) return false;

// This can return false positive, for example for /\(?<a>\)/.
// However, it's such a rare occurrence that it's ok to compile
// the regexp even if we only need to compile regexps with
// duplicate named capturing groups.
const regex = /\(\?<([^>]+)>/g;
const seen = new Set();
for (let match; (match = regex.exec(pattern)); seen.add(match[1])) {
if (seen.has(match[1])) return "transform";
}
return false;
};

return {
unicodeFlag: feat("unicodeFlag"),
unicodeSetsFlag:
feat<Experimental>("unicodeSetsFlag") ||
feat<Experimental>("unicodeSetsFlag_syntax", "parse"),
dotAllFlag: feat("dotAllFlag"),
unicodePropertyEscapes: feat("unicodePropertyEscape"),
namedGroups:
feat("namedCaptureGroups") || feat("duplicateNamedCaptureGroups"),
namedGroups: feat("namedCaptureGroups") || featDuplicateNamedGroups(),
onNamedGroup: () => {},
};
}
Expand Down

0 comments on commit 0e93aff

Please sign in to comment.