diff --git a/node/config.ts b/node/config.ts index e51df8f4..5977a4a2 100644 --- a/node/config.ts +++ b/node/config.ts @@ -29,7 +29,7 @@ export const enum Cache { export interface FunctionConfig { cache?: Cache - path?: string + path?: string | string[] } const getConfigExtractor = () => { diff --git a/node/declaration.test.ts b/node/declaration.test.ts index 20b94dae..4238a9c2 100644 --- a/node/declaration.test.ts +++ b/node/declaration.test.ts @@ -9,19 +9,20 @@ const deployConfig = { layers: [], } -test('In source config takes precedence over netlify.toml config', () => { +test('In-source config takes precedence over netlify.toml config', () => { const tomlConfig = [ { function: 'geolocation', path: '/geo', cache: 'off' }, { function: 'json', path: '/json', cache: 'manual' }, ] const funcConfig = { - geolocation: { path: '/geo-isc', cache: 'manual' }, + geolocation: { path: ['/geo-isc', '/*'], cache: 'manual' }, json: { path: '/json', cache: 'off' }, } as Record const expectedDeclarations = [ { function: 'geolocation', path: '/geo-isc', cache: 'manual' }, + { function: 'geolocation', path: '/*', cache: 'manual' }, { function: 'json', path: '/json', cache: 'off' }, ] @@ -30,14 +31,14 @@ test('In source config takes precedence over netlify.toml config', () => { expect(declarations).toEqual(expectedDeclarations) }) -test("Declarations don't break if no in source config is provided", () => { +test("Declarations don't break if no in-source config is provided", () => { const tomlConfig = [ { function: 'geolocation', path: '/geo', cache: 'off' }, { function: 'json', path: '/json', cache: 'manual' }, ] const funcConfig = { - geolocation: { path: '/geo-isc', cache: 'manual' }, + geolocation: { path: ['/geo-isc'], cache: 'manual' }, json: {}, } as Record @@ -51,11 +52,11 @@ test("Declarations don't break if no in source config is provided", () => { expect(declarations).toEqual(expectedDeclarations) }) -test('In source config works independent of the netlify.toml file if a path is defined and otherwise if no path is set', () => { +test('In-source config works independent of the netlify.toml file if a path is defined and otherwise if no path is set', () => { const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }] const funcConfigWithPath = { - json: { path: '/json', cache: 'off' }, + json: { path: ['/json', '/json-isc'], cache: 'off' }, } as Record const funcConfigWithoutPath = { @@ -65,6 +66,7 @@ test('In source config works independent of the netlify.toml file if a path is d const expectedDeclarationsWithISCPath = [ { function: 'geolocation', path: '/geo', cache: 'off' }, { function: 'json', path: '/json', cache: 'off' }, + { function: 'json', path: '/json-isc', cache: 'off' }, ] const expectedDeclarationsWithoutISCPath = [{ function: 'geolocation', path: '/geo', cache: 'off' }] @@ -75,3 +77,53 @@ test('In source config works independent of the netlify.toml file if a path is d const declarationsWithoutISCPath = getDeclarationsFromConfig(tomlConfig, funcConfigWithoutPath, deployConfig) expect(declarationsWithoutISCPath).toEqual(expectedDeclarationsWithoutISCPath) }) + +test('In-source config works if only the cache config property is set', () => { + const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }] + + const funcConfig = { + geolocation: { cache: 'manual' }, + } as Record + + const expectedDeclarations = [{ function: 'geolocation', path: '/geo', cache: 'manual' }] + + expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations) +}) + +test("In-source config path property works if it's not an array", () => { + const tomlConfig = [{ function: 'json', path: '/json-toml', cache: 'off' }] + + const funcConfig = { + json: { path: '/json', cache: 'manual' }, + } as Record + + const expectedDeclarations = [{ function: 'json', path: '/json', cache: 'manual' }] + + expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations) +}) + +test("In-source config path property works if it's not an array and it's not present in toml or deploy config", () => { + const tomlConfig = [{ function: 'geolocation', path: '/geo', cache: 'off' }] + const funcConfig = { + json: { path: '/json-isc', cache: 'manual' }, + } as Record + + const expectedDeclarations = [ + { function: 'geolocation', path: '/geo', cache: 'off' }, + { function: 'json', path: '/json-isc', cache: 'manual' }, + ] + + expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations) +}) + +test('In-source config works if path property is an empty array with cache value specified', () => { + const tomlConfig = [{ function: 'json', path: '/json-toml', cache: 'off' }] + + const funcConfig = { + json: { path: [], cache: 'manual' }, + } as Record + + const expectedDeclarations = [{ function: 'json', path: '/json-toml', cache: 'manual' }] + + expect(getDeclarationsFromConfig(tomlConfig, funcConfig, deployConfig)).toEqual(expectedDeclarations) +}) diff --git a/node/declaration.ts b/node/declaration.ts index e8fc340a..d0edec04 100644 --- a/node/declaration.ts +++ b/node/declaration.ts @@ -30,19 +30,44 @@ export const getDeclarationsFromConfig = ( // a function configuration object, we replace the path because that object // takes precedence. for (const declaration of [...tomlDeclarations, ...deployConfig.declarations]) { - const config = functionsConfig[declaration.function] ?? {} + const config = functionsConfig[declaration.function] + + // If no config is found, add the declaration as is + if (!config) { + declarations.push(declaration) + + // If we have a path specified as either a string or non-empty array + // create a declaration for each path + } else if (config.path?.length) { + const paths = Array.isArray(config.path) ? config.path : [config.path] + + paths.forEach((path) => { + declarations.push({ ...declaration, ...config, path }) + }) + + // With an in-source config without a path, add the config to the declaration + } else { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { path, ...rest } = config + declarations.push({ ...declaration, ...rest }) + } functionsVisited.add(declaration.function) - declarations.push({ ...declaration, ...config }) } // Finally, we must create declarations for functions that are not declared // in the TOML at all. for (const name in functionsConfig) { - const { path, ...config } = functionsConfig[name] + const { ...config } = functionsConfig[name] + const { path } = functionsConfig[name] + // If we have path specified create a declaration for each path if (!functionsVisited.has(name) && path) { - declarations.push({ ...config, function: name, path }) + const paths = Array.isArray(path) ? path : [path] + + paths.forEach((singlePath) => { + declarations.push({ ...config, function: name, path: singlePath }) + }) } } diff --git a/package.json b/package.json index 52baa772..c4306f73 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "test:dev": "run-s test:dev:*", "test:ci": "run-s test:ci:*", "test:dev:vitest": "cross-env FORCE_COLOR=0 vitest run", + "test:dev:vitest:watch": "cross-env FORCE_COLOR=0 vitest watch", "test:dev:deno": "deno test --allow-all deno", "test:ci:vitest": "cross-env FORCE_COLOR=0 vitest run", "test:ci:deno": "deno test --allow-all deno",