From b5ec4057eb8e7897360c80011d5b2f46ddbb58ab Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Fri, 16 Dec 2022 23:37:19 -0500 Subject: [PATCH] support the `v` regular expression literal flag --- CHANGELOG.md | 4 ++++ internal/compat/js_table.go | 3 +++ internal/js_lexer/js_lexer.go | 2 +- internal/js_parser/js_parser.go | 6 ++++++ scripts/compat-table.js | 1 + scripts/js-api-tests.js | 4 ++++ 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cdd82e52cf..15822c93c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,10 @@ This fix was contributed by [@fz6m](https://github.com/fz6m). +* Add support for the `v` flag in regular expression literals + + People are currently working on adding a `v` flag to JavaScript regular expresions. You can read more about this flag here: https://v8.dev/features/regexp-v-flag. This release adds support for parsing this flag, so esbuild will now no longer consider regular expression literals with this flag to be a syntax error. If the target is set to something other than `esnext`, esbuild will transform regular expression literals containing this flag into a `new RegExp()` constructor call so the resulting code doesn't have a syntax error. This enables you to provide a polyfill for `RegExp` that implements the `v` flag to get your code to work at run-time. While esbuild doesn't typically adopt proposals until they're already shipping in a real JavaScript run-time, I'm adding it now because a) esbuild's implementation doesn't need to change as the proposal evolves, b) this isn't really new syntax since regular expression literals already have flags, and c) esbuild's implementation is a trivial pass-through anyway. + ## 0.16.8 * Allow plugins to resolve injected files ([#2754](https://github.com/evanw/esbuild/issues/2754)) diff --git a/internal/compat/js_table.go b/internal/compat/js_table.go index 76468f1ecf3..e2b5e6040b3 100644 --- a/internal/compat/js_table.go +++ b/internal/compat/js_table.go @@ -97,6 +97,7 @@ const ( RegexpLookbehindAssertions RegexpMatchIndices RegexpNamedCaptureGroups + RegexpSetNotation RegexpStickyAndUnicodeFlags RegexpUnicodePropertyEscapes RestArgument @@ -152,6 +153,7 @@ var StringToJSFeature = map[string]JSFeature{ "regexp-lookbehind-assertions": RegexpLookbehindAssertions, "regexp-match-indices": RegexpMatchIndices, "regexp-named-capture-groups": RegexpNamedCaptureGroups, + "regexp-set-notation": RegexpSetNotation, "regexp-sticky-and-unicode-flags": RegexpStickyAndUnicodeFlags, "regexp-unicode-property-escapes": RegexpUnicodePropertyEscapes, "rest-argument": RestArgument, @@ -633,6 +635,7 @@ var jsTable = map[JSFeature]map[Engine][]versionRange{ Opera: {{start: v{51, 0, 0}}}, Safari: {{start: v{11, 1, 0}}}, }, + RegexpSetNotation: {}, RegexpStickyAndUnicodeFlags: { Chrome: {{start: v{50, 0, 0}}}, Deno: {{start: v{1, 0, 0}}}, diff --git a/internal/js_lexer/js_lexer.go b/internal/js_lexer/js_lexer.go index aeeceff3eb0..3fc7ac093f3 100644 --- a/internal/js_lexer/js_lexer.go +++ b/internal/js_lexer/js_lexer.go @@ -2042,7 +2042,7 @@ func (lexer *Lexer) ScanRegExp() { bits := uint32(0) for js_ast.IsIdentifierContinue(lexer.codePoint) { switch lexer.codePoint { - case 'd', 'g', 'i', 'm', 's', 'u', 'y': + case 'd', 'g', 'i', 'm', 's', 'u', 'v', 'y': bit := uint32(1) << uint32(lexer.codePoint-'a') if (bit & bits) != 0 { // Reject duplicate flags diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index a4b60975884..2c4550d9b3c 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -11643,6 +11643,12 @@ pattern: } feature = compat.RegexpMatchIndices + case 'v': + if !p.options.unsupportedJSFeatures.Has(compat.RegexpSetNotation) { + continue // This is from a proposal: https://github.com/tc39/proposal-regexp-v-flag + } + feature = compat.RegexpSetNotation + default: // Unknown flags are never supported } diff --git a/scripts/compat-table.js b/scripts/compat-table.js index 1dad6e4639e..43d15c5264c 100644 --- a/scripts/compat-table.js +++ b/scripts/compat-table.js @@ -204,6 +204,7 @@ mergeVersions('ClassStaticField', { es2022: true }) mergeVersions('TopLevelAwait', { es2022: true }) mergeVersions('ArbitraryModuleNamespaceNames', { es2022: true }) mergeVersions('RegexpMatchIndices', { es2022: true }) +mergeVersions('RegexpSetNotation', {}) mergeVersions('ImportAssertions', {}) // Manually copied from https://caniuse.com/?search=export%20*%20as diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index d63de9a647b..deabb490c57 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -5453,6 +5453,10 @@ let transformTests = { // RegExpMatchIndices check('es2022', `x1 = /y/d`, `x1 = /y/d;\n`), check('es2021', `x2 = /y/d`, `x2 = new RegExp("y", "d");\n`), + + // RegExpSetNotation + check('esnext', `x1 = /[\\p{White_Space}&&\\p{ASCII}]/v`, `x1 = /[\\p{White_Space}&&\\p{ASCII}]/v;\n`), + check('es2022', `x2 = /[\\p{White_Space}&&\\p{ASCII}]/v`, `x2 = new RegExp("[\\\\p{White_Space}&&\\\\p{ASCII}]", "v");\n`), ]) },