This repository has been archived by the owner on May 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 35
/
index.ts
136 lines (118 loc) · 4.57 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import { extname, basename } from 'path'
import { Config, getConfigForFunction } from '../config'
import { defaultFlags, FeatureFlags } from '../feature_flags'
import { FunctionSource } from '../function'
import { FsCache } from '../utils/fs'
import goRuntime from './go'
import jsRuntime from './node'
import type { Runtime } from './runtime'
import rustRuntime from './rust'
// A `Map` of functions, indexed by their name.
type FunctionMap = Map<string, FunctionSource>
// A tuple containing the name of a function and the object describing it.
// This is compatible with the constructor of `FunctionMap`.
type FunctionTuple = [string, FunctionSource]
// The same as `FunctionTuple` but functions don't have a `config` object yet.
type FunctionTupleWithoutConfig = [string, Omit<FunctionSource, 'config'>]
/**
* Finds functions for a list of paths using a specific runtime. The return
* value is an object containing an array of the functions found (`functions`)
* and an array with the paths that haven't been recognized by the runtime
* (`remainingPaths`).
*/
const findFunctionsInRuntime = async function ({
dedupe = false,
featureFlags,
fsCache,
paths,
runtime,
}: {
dedupe: boolean
featureFlags: FeatureFlags
fsCache: FsCache
paths: string[]
runtime: Runtime
}) {
const functions = await runtime.findFunctionsInPaths({ featureFlags, fsCache, paths })
// If `dedupe` is true, we use the function name (`filename`) as the map key,
// so that `function-1.js` will overwrite `function-1.go`. Otherwise, we use
// `srcPath`, so that both functions are returned.
const key = dedupe ? 'name' : 'srcPath'
// Augmenting the function objects with additional information.
const augmentedFunctions: FunctionTupleWithoutConfig[] = functions.map((func) => [
func[key],
{
...func,
extension: extname(func.mainFile),
filename: basename(func.srcPath),
runtime,
},
])
const usedPaths = new Set(augmentedFunctions.map(([path]) => path))
const remainingPaths = paths.filter((path) => !usedPaths.has(path))
return { functions: augmentedFunctions, remainingPaths }
}
// An object to cache filesystem operations. This allows different functions
// to perform IO operations on the same file (i.e. getting its stats or its
// contents) without duplicating work.
const makeFsCache = (): FsCache => ({})
// The order of this array determines the priority of the runtimes. If a path
// is used by the first time, it won't be made available to the subsequent
// runtimes.
const RUNTIMES = [jsRuntime, goRuntime, rustRuntime]
/**
* Gets a list of functions found in a list of paths.
*/
export const getFunctionsFromPaths = async (
paths: string[],
{
config,
dedupe = false,
featureFlags = defaultFlags,
}: { config?: Config; dedupe?: boolean; featureFlags?: FeatureFlags } = {},
): Promise<FunctionMap> => {
const fsCache = makeFsCache()
// We cycle through the ordered array of runtimes, passing each one of them
// through `findFunctionsInRuntime`. For each iteration, we collect all the
// functions found plus the list of paths that still need to be evaluated,
// using them as the input for the next iteration until the last runtime.
const { functions } = await RUNTIMES.reduce(async (aggregate, runtime) => {
const { functions: aggregateFunctions, remainingPaths: aggregatePaths } = await aggregate
const { functions: runtimeFunctions, remainingPaths: runtimePaths } = await findFunctionsInRuntime({
dedupe,
featureFlags,
fsCache,
paths: aggregatePaths,
runtime,
})
return {
functions: [...aggregateFunctions, ...runtimeFunctions],
remainingPaths: runtimePaths,
}
}, Promise.resolve({ functions: [], remainingPaths: paths } as { functions: FunctionTupleWithoutConfig[]; remainingPaths: string[] }))
const functionsWithConfig: FunctionTuple[] = functions.map(([name, func]) => [
name,
{ ...func, config: getConfigForFunction({ config, func }) },
])
return new Map(functionsWithConfig)
}
/**
* Gets a list of functions found in a list of paths.
*/
export const getFunctionFromPath = async (
path: string,
{ config, featureFlags = defaultFlags }: { config?: Config; featureFlags?: FeatureFlags } = {},
): Promise<FunctionSource | undefined> => {
const fsCache = makeFsCache()
for (const runtime of RUNTIMES) {
const func = await runtime.findFunctionInPath({ path, fsCache, featureFlags })
if (func) {
return {
...func,
runtime,
config: getConfigForFunction({ config, func: { ...func, runtime } }),
}
}
}
return undefined
}