-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add excludedPath
support to ISC & TOML
#402
Changes from all commits
e0659f8
ec67991
49a941b
d8f6b46
00a41b3
f972a69
65435cf
3476b72
9e92fc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -342,14 +342,15 @@ test('Loads declarations and import maps from the deploy configuration and in-so | |
|
||
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8') | ||
const manifest = JSON.parse(manifestFile) | ||
const { bundles, function_config: functionConfig } = manifest | ||
const { bundles, function_config: functionConfig, routes } = manifest | ||
expect(bundles.length).toBe(1) | ||
expect(bundles[0].format).toBe('eszip2') | ||
expect(generatedFiles.includes(bundles[0].asset)).toBe(true) | ||
|
||
// respects excludedPath from deploy config | ||
expect(routes[0].excluded_patterns).toEqual(['^/func2/skip/?$']) | ||
|
||
expect(functionConfig.func2).toEqual({ | ||
excluded_patterns: ['^/func2/skip/?$'], | ||
name: 'Function two', | ||
generator: '@netlify/[email protected]', | ||
}) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -193,15 +193,15 @@ test('Loads function paths from the in-source `config` function', async () => { | |
expect(generatedFiles.includes(bundles[0].asset)).toBe(true) | ||
|
||
expect(routes.length).toBe(6) | ||
expect(routes[0]).toEqual({ function: 'framework-func2', pattern: '^/framework-func2/?$' }) | ||
expect(routes[1]).toEqual({ function: 'user-func2', pattern: '^/user-func2/?$' }) | ||
expect(routes[2]).toEqual({ function: 'framework-func1', pattern: '^/framework-func1/?$' }) | ||
expect(routes[3]).toEqual({ function: 'user-func1', pattern: '^/user-func1/?$' }) | ||
expect(routes[4]).toEqual({ function: 'user-func3', pattern: '^/user-func3/?$' }) | ||
expect(routes[5]).toEqual({ function: 'user-func5', pattern: '^/user-func5/.*/?$' }) | ||
expect(routes[0]).toEqual({ function: 'framework-func2', pattern: '^/framework-func2/?$', excluded_patterns: [] }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even if the user doesn't configure an |
||
expect(routes[1]).toEqual({ function: 'user-func2', pattern: '^/user-func2/?$', excluded_patterns: [] }) | ||
expect(routes[2]).toEqual({ function: 'framework-func1', pattern: '^/framework-func1/?$', excluded_patterns: [] }) | ||
expect(routes[3]).toEqual({ function: 'user-func1', pattern: '^/user-func1/?$', excluded_patterns: [] }) | ||
expect(routes[4]).toEqual({ function: 'user-func3', pattern: '^/user-func3/?$', excluded_patterns: [] }) | ||
expect(routes[5]).toEqual({ function: 'user-func5', pattern: '^/user-func5/.*/?$', excluded_patterns: [] }) | ||
|
||
expect(postCacheRoutes.length).toBe(1) | ||
expect(postCacheRoutes[0]).toEqual({ function: 'user-func4', pattern: '^/user-func4/?$' }) | ||
expect(postCacheRoutes[0]).toEqual({ function: 'user-func4', pattern: '^/user-func4/?$', excluded_patterns: [] }) | ||
|
||
expect(Object.keys(functionConfig)).toHaveLength(1) | ||
expect(functionConfig['user-func5']).toEqual({ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,12 +13,12 @@ interface BaseDeclaration { | |
|
||
type DeclarationWithPath = BaseDeclaration & { | ||
path: Path | ||
excludedPath?: Path | ||
excludedPath?: Path | Path[] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
type DeclarationWithPattern = BaseDeclaration & { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. absolutely! done in 9e92fc8 |
||
pattern: string | ||
excludedPattern?: string | ||
excludedPattern?: string | string[] | ||
} | ||
|
||
export type Declaration = DeclarationWithPath | DeclarationWithPattern | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,7 +28,7 @@ test('Generates a manifest with different bundles', () => { | |
{ asset: bundle1.hash + bundle1.extension, format: bundle1.format }, | ||
{ asset: bundle2.hash + bundle2.extension, format: bundle2.format }, | ||
] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$' }] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [] }] | ||
|
||
expect(manifest.bundles).toEqual(expectedBundles) | ||
expect(manifest.routes).toEqual(expectedRoutes) | ||
|
@@ -46,7 +46,7 @@ test('Generates a manifest with display names', () => { | |
} | ||
const manifest = generateManifest({ bundles: [], declarations, functions, internalFunctionConfig }) | ||
|
||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$' }] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$', excluded_patterns: [] }] | ||
expect(manifest.function_config).toEqual({ | ||
'func-1': { name: 'Display Name' }, | ||
}) | ||
|
@@ -65,7 +65,7 @@ test('Generates a manifest with a generator field', () => { | |
} | ||
const manifest = generateManifest({ bundles: [], declarations, functions, internalFunctionConfig }) | ||
|
||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$' }] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$', excluded_patterns: [] }] | ||
const expectedFunctionConfig = { 'func-1': { generator: '@netlify/[email protected]' } } | ||
expect(manifest.routes).toEqual(expectedRoutes) | ||
expect(manifest.function_config).toEqual(expectedFunctionConfig) | ||
|
@@ -79,22 +79,18 @@ test('Generates a manifest with excluded paths and patterns', () => { | |
] | ||
const declarations: Declaration[] = [ | ||
{ function: 'func-1', path: '/f1/*', excludedPath: '/f1/exclude' }, | ||
{ function: 'func-2', pattern: '^/f2/.*/?$', excludedPattern: '^/f2/exclude$' }, | ||
{ function: 'func-2', pattern: '^/f2/.*/?$', excludedPattern: ['^/f2/exclude$', '^/f2/exclude-as-well$'] }, | ||
{ function: 'func-3', path: '/*', excludedPath: '/**/*.html' }, | ||
] | ||
const manifest = generateManifest({ bundles: [], declarations, functions }) | ||
const expectedRoutes = [ | ||
{ function: 'func-1', pattern: '^/f1/.*/?$' }, | ||
{ function: 'func-2', pattern: '^/f2/.*/?$' }, | ||
{ function: 'func-3', pattern: '^/.*/?$' }, | ||
{ function: 'func-1', pattern: '^/f1/.*/?$', excluded_patterns: ['^/f1/exclude/?$'] }, | ||
{ function: 'func-2', pattern: '^/f2/.*/?$', excluded_patterns: ['^/f2/exclude$', '^/f2/exclude-as-well$'] }, | ||
{ function: 'func-3', pattern: '^/.*/?$', excluded_patterns: ['^/.*/.*\\.html/?$'] }, | ||
] | ||
|
||
expect(manifest.routes).toEqual(expectedRoutes) | ||
expect(manifest.function_config).toEqual({ | ||
'func-1': { excluded_patterns: ['^/f1/exclude/?$'] }, | ||
'func-2': { excluded_patterns: ['^/f2/exclude$'] }, | ||
'func-3': { excluded_patterns: ['^/.*/.*\\.html/?$'] }, | ||
}) | ||
expect(manifest.function_config).toEqual({}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since |
||
expect(manifest.bundler_version).toBe(env.npm_package_version as string) | ||
|
||
const matcher = getRouteMatcher(manifest) | ||
|
@@ -111,7 +107,7 @@ test('TOML-defined paths can be combined with ISC-defined excluded paths', () => | |
} | ||
const manifest = generateManifest({ bundles: [], declarations, functions, userFunctionConfig }) | ||
|
||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$' }] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/.*/?$', excluded_patterns: [] }] | ||
|
||
expect(manifest.routes).toEqual(expectedRoutes) | ||
expect(manifest.function_config).toEqual({ | ||
|
@@ -171,6 +167,52 @@ test('Filters out internal in-source configurations in user created functions', | |
}) | ||
}) | ||
|
||
test('excludedPath from ISC goes into function_config, TOML goes into routes', () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This test matches the scenario described in the proposal here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So the |
||
const functions = [{ name: 'customisation', path: '/path/to/customisation.ts' }] | ||
const declarations: Declaration[] = [ | ||
{ function: 'customisation', path: '/showcases/*' }, | ||
{ function: 'customisation', path: '/checkout/*', excludedPath: ['/*/terms-and-conditions'] }, | ||
] | ||
const userFunctionConfig: Record<string, FunctionConfig> = { | ||
customisation: { | ||
excludedPath: ['/*.css', '/*.jpg'], | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the user configures There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've understood it the same way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that's correct. Context in #402 (comment). |
||
} | ||
const internalFunctionConfig: Record<string, FunctionConfig> = {} | ||
const manifest = generateManifest({ | ||
bundles: [], | ||
declarations, | ||
functions, | ||
userFunctionConfig, | ||
internalFunctionConfig, | ||
}) | ||
expect(manifest.routes).toEqual([ | ||
{ | ||
function: 'customisation', | ||
pattern: '^/showcases/.*/?$', | ||
excluded_patterns: [], | ||
}, | ||
{ | ||
function: 'customisation', | ||
pattern: '^/checkout/.*/?$', | ||
excluded_patterns: ['^/.*/terms-and-conditions/?$'], | ||
}, | ||
]) | ||
expect(manifest.function_config).toEqual({ | ||
customisation: { | ||
excluded_patterns: ['^/.*\\.css/?$', '^/.*\\.jpg/?$'], | ||
}, | ||
}) | ||
|
||
const matcher = getRouteMatcher(manifest) | ||
|
||
expect(matcher('/showcases/boho-style')).toBeDefined() | ||
expect(matcher('/checkout/address')).toBeDefined() | ||
expect(matcher('/checkout/terms-and-conditions')).toBeUndefined() | ||
expect(matcher('/checkout/scrooge-mc-duck-animation.css')).toBeUndefined() | ||
expect(matcher('/showcases/boho-style/expensive-chair.jpg')).toBeUndefined() | ||
}) | ||
|
||
test('Includes failure modes in manifest', () => { | ||
const functions = [ | ||
{ name: 'func-1', path: '/path/to/func-1.ts' }, | ||
|
@@ -204,7 +246,7 @@ test('Excludes functions for which there are function files but no matching conf | |
const declarations: Declaration[] = [{ function: 'func-1', path: '/f1' }] | ||
const manifest = generateManifest({ bundles: [bundle1], declarations, functions }) | ||
|
||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$' }] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [] }] | ||
|
||
expect(manifest.routes).toEqual(expectedRoutes) | ||
}) | ||
|
@@ -222,7 +264,7 @@ test('Excludes functions for which there are config declarations but no matching | |
] | ||
const manifest = generateManifest({ bundles: [bundle1], declarations, functions }) | ||
|
||
const expectedRoutes = [{ function: 'func-2', pattern: '^/f2/?$' }] | ||
const expectedRoutes = [{ function: 'func-2', pattern: '^/f2/?$', excluded_patterns: [] }] | ||
|
||
expect(manifest.routes).toEqual(expectedRoutes) | ||
}) | ||
|
@@ -232,7 +274,7 @@ test('Generates a manifest without bundles', () => { | |
const declarations: Declaration[] = [{ function: 'func-1', path: '/f1' }] | ||
const manifest = generateManifest({ bundles: [], declarations, functions }) | ||
|
||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$' }] | ||
const expectedRoutes = [{ function: 'func-1', pattern: '^/f1/?$', excluded_patterns: [] }] | ||
|
||
expect(manifest.bundles).toEqual([]) | ||
expect(manifest.routes).toEqual(expectedRoutes) | ||
|
@@ -267,10 +309,10 @@ test('Generates a manifest with pre and post-cache routes', () => { | |
{ asset: bundle2.hash + bundle2.extension, format: bundle2.format }, | ||
] | ||
const expectedPreCacheRoutes = [ | ||
{ function: 'func-1', name: undefined, pattern: '^/f1/?$' }, | ||
{ function: 'func-2', name: undefined, pattern: '^/f2/?$' }, | ||
{ function: 'func-1', name: undefined, pattern: '^/f1/?$', excluded_patterns: [] }, | ||
{ function: 'func-2', name: undefined, pattern: '^/f2/?$', excluded_patterns: [] }, | ||
] | ||
const expectedPostCacheRoutes = [{ function: 'func-3', name: undefined, pattern: '^/f3/?$' }] | ||
const expectedPostCacheRoutes = [{ function: 'func-3', name: undefined, pattern: '^/f3/?$', excluded_patterns: [] }] | ||
|
||
expect(manifest.bundles).toEqual(expectedBundles) | ||
expect(manifest.routes).toEqual(expectedPreCacheRoutes) | ||
|
@@ -288,8 +330,8 @@ test('Generates a manifest with layers', () => { | |
{ function: 'func-2', path: '/f2/*' }, | ||
] | ||
const expectedRoutes = [ | ||
{ function: 'func-1', pattern: '^/f1/.*/?$' }, | ||
{ function: 'func-2', pattern: '^/f2/.*/?$' }, | ||
{ function: 'func-1', pattern: '^/f1/.*/?$', excluded_patterns: [] }, | ||
{ function: 'func-2', pattern: '^/f2/.*/?$', excluded_patterns: [] }, | ||
] | ||
const layers = [ | ||
{ | ||
|
@@ -323,7 +365,7 @@ test('Shows a warning if the regular expression contains a negative lookahead', | |
|
||
console.warn = consoleWarn | ||
|
||
expect(manifest.routes).toEqual([{ function: 'func-1', pattern: '^/\\w+(?=\\d)$' }]) | ||
expect(manifest.routes).toEqual([{ function: 'func-1', pattern: '^/\\w+(?=\\d)$', excluded_patterns: [] }]) | ||
expect(mockConsoleWarn).toHaveBeenCalledOnce() | ||
expect(mockConsoleWarn).toHaveBeenCalledWith( | ||
"Function 'func-1' uses an unsupported regular expression and will not be invoked: Regular expressions with lookaheads are not supported", | ||
|
@@ -351,5 +393,5 @@ test('Converts named capture groups to unnamed capture groups in regular express | |
const declarations = [{ function: 'func-1', pattern: '^/(?<name>\\w+)$' }] | ||
const manifest = generateManifest({ bundles: [], declarations, functions }) | ||
|
||
expect(manifest.routes).toEqual([{ function: 'func-1', pattern: '^/(\\w+)$' }]) | ||
expect(manifest.routes).toEqual([{ function: 'func-1', pattern: '^/(\\w+)$', excluded_patterns: [] }]) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,8 +52,12 @@ const getRouteMatcher = (manifest: Manifest) => (candidate: string) => | |
return false | ||
} | ||
|
||
const excludedPattern = manifest.function_config[route.function].excluded_patterns | ||
const isExcluded = excludedPattern.some((pattern) => new RegExp(pattern).test(candidate)) | ||
if (route.excluded_patterns.some((pattern) => new RegExp(pattern).test(candidate))) { | ||
return false | ||
} | ||
|
||
const excludedPatterns = manifest.function_config[route.function]?.excluded_patterns ?? [] | ||
const isExcluded = excludedPatterns.some((pattern) => new RegExp(pattern).test(candidate)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if the regex is invalid then it will throw here with an Should we handle this case, or is the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm unsure about that. This is unrelated to the PR, so let's open an issue and address it in another PR :) |
||
|
||
return !isExcluded | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Thinking aloud as I follow along to make sure I understand what's happening)
So, previously the
excluded_patterns
was part of thefunctionConfig
, but with this change it moves to theroutes
... unless the user sets it in the ISC, in which case it'll still be set in the
functionConfig
, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right! Edge Functions can serve different routes, and so we make the distinction between "route config" and "function config". Route Config only applies to executions of a function that match that route, while function config will be applied to all executions, no matter the route.