Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

feat: add support to per-function configuration files #1030

Merged
merged 9 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"find-up": "^5.0.0",
"glob": "^8.0.3",
"is-builtin-module": "^3.1.0",
"is-path-inside": "^3.0.3",
"junk": "^3.1.0",
"locate-path": "^6.0.0",
"merge-options": "^3.0.4",
Expand Down
71 changes: 67 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { promises as fs } from 'fs'
import { basename, extname, dirname, join } from 'path'

import isPathInside from 'is-path-inside'
import mergeOptions from 'merge-options'

import type { FeatureFlags } from './feature_flags.js'
import { FunctionSource } from './function.js'
import type { NodeBundlerName } from './runtimes/node/bundlers/types.js'
import type { NodeVersionString } from './runtimes/node/index.js'
import { minimatch } from './utils/matching.js'

export interface FunctionConfig {
interface FunctionConfig {
externalNodeModules?: string[]
includedFiles?: string[]
includedFilesBasePath?: string
danez marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -19,16 +24,54 @@ export interface FunctionConfig {
zipGo?: boolean
}

interface FunctionConfigFile {
config: FunctionConfig
version: number
}

type GlobPattern = string

export type Config = Record<GlobPattern, FunctionConfig>
type Config = Record<GlobPattern, FunctionConfig>
type FunctionWithoutConfig = Omit<FunctionSource, 'config'>

const getConfigForFunction = async ({
config,
configFileDirectories,
func,
featureFlags,
}: {
config?: Config
configFileDirectories?: string[]
func: FunctionWithoutConfig
featureFlags: FeatureFlags
}): Promise<FunctionConfig> => {
const fromConfig = getFromMainConfig({ config, func })

// We try to read from a function config file if the function directory is
// inside one of `configFileDirectories`.
const shouldReadConfigFile =
featureFlags.project_deploy_configuration_api_use_per_function_configuration_files &&
danez marked this conversation as resolved.
Show resolved Hide resolved
configFileDirectories?.some((directory) => isPathInside(func.mainFile, directory))

if (!shouldReadConfigFile) {
return fromConfig
}

const fromFile = await getFromFile(func)

return {
...fromConfig,
...fromFile,
}
}

export const getConfigForFunction = ({
const getFromMainConfig = ({
config,
func,
}: {
config?: Config
func: Omit<FunctionSource, 'config'>
configFileDirectories?: string[]
func: FunctionWithoutConfig
}): FunctionConfig => {
if (!config) {
return {}
Expand Down Expand Up @@ -56,3 +99,23 @@ export const getConfigForFunction = ({

return mergeOptions.apply({ concatArrays: true, ignoreUndefined: true }, matches)
}

const getFromFile = async (func: FunctionWithoutConfig): Promise<FunctionConfig> => {
const filename = `${basename(func.mainFile, extname(func.mainFile))}.json`
const configFilePath = join(dirname(func.mainFile), filename)

try {
const data = await fs.readFile(configFilePath, 'utf8')
const configFile = JSON.parse(data) as FunctionConfigFile

if (configFile.version === 1) {
danez marked this conversation as resolved.
Show resolved Hide resolved
return configFile.config
}
} catch {
// no-op
}

return {}
}

export { Config, FunctionConfig, FunctionWithoutConfig, getConfigForFunction }
1 change: 1 addition & 0 deletions src/feature_flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const defaultFlags: Record<string, boolean> = {
parseWithEsbuild: false,
traceWithNft: false,
zisi_pure_esm: false,
project_deploy_configuration_api_use_per_function_configuration_files: false,
}

export type FeatureFlag = keyof typeof defaultFlags
Expand Down
18 changes: 12 additions & 6 deletions src/runtimes/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { extname, basename } from 'path'

import { Config, getConfigForFunction } from '../config.js'
import { Config, getConfigForFunction, FunctionWithoutConfig } from '../config.js'
import { defaultFlags, FeatureFlags } from '../feature_flags.js'
import { FunctionSource } from '../function.js'
import { FsCache } from '../utils/fs.js'
Expand All @@ -18,7 +18,7 @@ type FunctionMap = Map<string, FunctionSource>
type FunctionTuple = [string, FunctionSource]

// The same as `FunctionTuple` but functions don't have a `config` object yet.
type FunctionTupleWithoutConfig = [string, Omit<FunctionSource, 'config'>]
type FunctionTupleWithoutConfig = [string, FunctionWithoutConfig]

/**
* Finds functions for a list of paths using a specific runtime. The return
Expand Down Expand Up @@ -79,9 +79,10 @@ export const getFunctionsFromPaths = async (
paths: string[],
{
config,
configFileDirectories = [],
dedupe = false,
featureFlags = defaultFlags,
}: { config?: Config; dedupe?: boolean; featureFlags?: FeatureFlags } = {},
}: { config?: Config; configFileDirectories?: string[]; dedupe?: boolean; featureFlags?: FeatureFlags } = {},
): Promise<FunctionMap> => {
const fsCache = makeFsCache()

Expand All @@ -104,9 +105,12 @@ export const getFunctionsFromPaths = async (
remainingPaths: runtimePaths,
}
}, Promise.resolve({ functions: [], remainingPaths: paths } as { functions: FunctionTupleWithoutConfig[]; remainingPaths: string[] }))
const functionsWithConfig: FunctionTuple[] = functions.map(([name, func]) => [
const functionConfigs = await Promise.all(
functions.map(([, func]) => getConfigForFunction({ config, configFileDirectories, func, featureFlags })),
)
const functionsWithConfig: FunctionTuple[] = functions.map(([name, func], index) => [
name,
{ ...func, config: getConfigForFunction({ config, func }) },
{ ...func, config: functionConfigs[index] },
])

return new Map(functionsWithConfig)
Expand All @@ -125,10 +129,12 @@ export const getFunctionFromPath = async (
const func = await runtime.findFunctionInPath({ path, fsCache, featureFlags })

if (func) {
const functionConfig = await getConfigForFunction({ config, func: { ...func, runtime }, featureFlags })

return {
...func,
runtime,
config: getConfigForFunction({ config, func: { ...func, runtime } }),
config: functionConfig,
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/zip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface ZipFunctionOptions {
}

type ZipFunctionsOptions = ZipFunctionOptions & {
configFileDirectories?: string[]
manifest?: string
parallelLimit?: number
}
Expand All @@ -46,6 +47,7 @@ export const zipFunctions = async function (
archiveFormat = 'zip',
basePath,
config = {},
configFileDirectories,
featureFlags: inputFeatureFlags,
manifest,
parallelLimit = DEFAULT_PARALLEL_LIMIT,
Expand All @@ -57,7 +59,7 @@ export const zipFunctions = async function (
const featureFlags = getFlags(inputFeatureFlags)
const srcFolders = resolveFunctionsDirectories(relativeSrcFolders)
const [paths] = await Promise.all([listFunctionsDirectories(srcFolders), fs.mkdir(destFolder, { recursive: true })])
const functions = await getFunctionsFromPaths(paths, { config, dedupe: true, featureFlags })
const functions = await getFunctionsFromPaths(paths, { config, configFileDirectories, dedupe: true, featureFlags })
const results = await pMap(
functions.values(),
async (func) => {
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"config": {
"includedFiles": ["blog/*.md"]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"config": {
"includedFiles": ["blog/*.md"]
},
"version": 3
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
"config": {
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"config": {
"includedFiles": ["blog/*.md"]
}
"version": 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"config": {
"includedFiles": ["blog/*.md"]
},
"version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"config": {
"includedFiles": ["blog/*.md"]
},
"version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"config": {
"includedFiles": ["blog/*.md"],
"schedule": "@daily"
},
"version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const handler = () => true
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"config": {
"includedFiles": ["blog/*.md"]
},
"version": 1
}