Skip to content

Commit

Permalink
feat: support PCRE regexp engine (#563)
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoboucas committed Jan 15, 2024
1 parent 1daecc6 commit 14ecd17
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 7 deletions.
4 changes: 3 additions & 1 deletion node/feature_flags.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const defaultFlags = {}
const defaultFlags = {
edge_bundler_pcre_regexp: false,
}

type FeatureFlag = keyof typeof defaultFlags
type FeatureFlags = Partial<Record<FeatureFlag, boolean>>
Expand Down
20 changes: 18 additions & 2 deletions node/manifest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,9 +433,9 @@ test('Generates a manifest with layers', () => {
expect(manifest2.layers).toEqual(layers)
})

test('Throws an error if the regular expression contains a negative lookahead', () => {
test('Throws an error if the regular expression contains a negative lookahead and support for the PCRE engine is disabled', () => {
const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }]
const declarations = [{ function: 'func-1', pattern: '^/\\w+(?=\\d)$' }]
const declarations = [{ function: 'func-1', pattern: '^\\/((?!api|_next\\/static|_next\\/image|favicon.ico).*)$' }]

expect(() =>
generateManifest({
Expand All @@ -448,6 +448,22 @@ test('Throws an error if the regular expression contains a negative lookahead',
)
})

test('Accepts regular expressions with lookaheads if support for the PCRE engine is enabled', () => {
const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }]
const declarations = [{ function: 'func-1', pattern: '^\\/((?!api|_next\\/static|_next\\/image|favicon.ico).*)$' }]
const { manifest } = generateManifest({
bundles: [],
declarations,
featureFlags: { edge_bundler_pcre_regexp: true },
functions,
})
const [route] = manifest.routes
const regexp = new RegExp(route.pattern)

expect(regexp.test('/foo')).toBeTruthy()
expect(regexp.test('/_next/static/foo')).toBeFalsy()
})

test('Converts named capture groups to unnamed capture groups in regular expressions', () => {
const functions = [{ name: 'func-1', path: '/path/to/func-1.ts' }]
const declarations = [{ function: 'func-1', pattern: '^/(?<name>\\w+)$' }]
Expand Down
18 changes: 14 additions & 4 deletions node/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const normalizeMethods = (method: unknown, name: string): string[] | undefined =
const generateManifest = ({
bundles = [],
declarations = [],
featureFlags,
functions,
userFunctionConfig = {},
internalFunctionConfig = {},
Expand Down Expand Up @@ -153,7 +154,7 @@ const generateManifest = ({
return
}

const pattern = getRegularExpression(declaration)
const pattern = getRegularExpression(declaration, Boolean(featureFlags?.edge_bundler_pcre_regexp))

// If there is no `pattern`, the declaration will never be triggered, so we
// can discard it.
Expand All @@ -163,7 +164,7 @@ const generateManifest = ({

routedFunctions.add(declaration.function)

const excludedPattern = getExcludedRegularExpressions(declaration)
const excludedPattern = getExcludedRegularExpressions(declaration, Boolean(featureFlags?.edge_bundler_pcre_regexp))
const route: Route = {
function: func.name,
pattern: serializePattern(pattern),
Expand Down Expand Up @@ -225,8 +226,12 @@ const pathToRegularExpression = (path: string) => {
}
}

const getRegularExpression = (declaration: Declaration) => {
const getRegularExpression = (declaration: Declaration, pcreRegexpEngine: boolean) => {
if ('pattern' in declaration) {
if (pcreRegexpEngine) {
return declaration.pattern
}

try {
return parsePattern(declaration.pattern)
} catch (error: unknown) {
Expand All @@ -241,11 +246,16 @@ const getRegularExpression = (declaration: Declaration) => {
return pathToRegularExpression(declaration.path)
}

const getExcludedRegularExpressions = (declaration: Declaration): string[] => {
const getExcludedRegularExpressions = (declaration: Declaration, pcreRegexpEngine: boolean): string[] => {
if ('excludedPattern' in declaration && declaration.excludedPattern) {
const excludedPatterns: string[] = Array.isArray(declaration.excludedPattern)
? declaration.excludedPattern
: [declaration.excludedPattern]

if (pcreRegexpEngine) {
return excludedPatterns
}

return excludedPatterns.map((excludedPattern) => {
try {
return parsePattern(excludedPattern)
Expand Down

0 comments on commit 14ecd17

Please sign in to comment.