EdgeFunctions.utils.ts106 lines · main
1import { common, dirname, relative } from '@std/path/posix'
2
3import { FileData } from '@/components/ui/FileExplorerAndEditor/FileExplorerAndEditor.types'
4import { EdgeFunctionBodyData } from '@/data/edge-functions/edge-function-body-query'
5
6export const getFallbackImportMapPath = (files: Omit<FileData, 'id' | 'content' | 'state'>[]) => {
7 // try to find a deno.json or import_map.json file
8 const regex = /^.*?(deno|import_map).json*$/i
9 return files.find(({ name }) => regex.test(name))?.name
10}
11
12export const getFallbackEntrypointPath = (files: Omit<FileData, 'id' | 'content' | 'state'>[]) => {
13 // when there's no matching entrypoint path is set,
14 // we use few heuristics to find an entrypoint file
15 // 1. If the function has only a single TS / JS file, if so set it as entrypoint
16 const jsFiles = files.filter(
17 ({ name }) =>
18 name.endsWith('.js') || name.endsWith('.ts') || name.endsWith('.jsx') || name.endsWith('.tsx')
19 )
20 if (jsFiles.length === 1) {
21 return jsFiles[0].name
22 } else if (jsFiles.length) {
23 // 2. If function has a `index` or `main` file use it as the entrypoint
24 const regex = /^.*?(index|main).*$/i
25 const matchingFile = jsFiles.find(({ name }) => regex.test(name))
26 // 3. if no valid index / main file found, we set the entrypoint expliclty to first JS file
27 return matchingFile ? matchingFile.name : jsFiles[0].name
28 } else {
29 // no potential entrypoint files found, this will most likely result in an error on deploy
30 return 'index.ts'
31 }
32}
33
34export const getStaticPatterns = (files: Omit<FileData, 'id' | 'content' | 'state'>[]) => {
35 return files
36 .filter(({ name }) => !name.match(/\.(js|ts|jsx|tsx|json|wasm)$/i))
37 .map(({ name }) => name)
38}
39
40function getBasePath(entrypoint: string | undefined, fileNames: string[]): string {
41 if (!entrypoint) {
42 return '/'
43 }
44
45 let candidate = fileNames.find((name) => entrypoint.endsWith(name))
46
47 if (candidate) {
48 return dirname(candidate)
49 } else {
50 try {
51 return dirname(new URL(entrypoint).pathname)
52 } catch (e) {
53 console.error('Failed to parse entrypoint', entrypoint)
54 return '/'
55 }
56 }
57}
58
59export const formatFunctionBodyToFiles = ({
60 functionBody,
61 entrypointPath,
62}: {
63 functionBody: EdgeFunctionBodyData
64 entrypointPath?: string
65}) => {
66 const entrypoint_path = functionBody.metadata?.deno2_entrypoint_path ?? entrypointPath
67
68 // Set files from API response when available
69 if (entrypoint_path) {
70 const base_path = getBasePath(
71 entrypoint_path,
72 functionBody.files.map((file) => file.name)
73 )
74 const filesWithRelPath = functionBody.files
75 // set file paths relative to entrypoint
76 .map((file: { name: string; content: string }) => {
77 try {
78 // if the current file and base path doesn't share a common path,
79 // return unmodified file
80 const common_path = common([base_path, file.name])
81 if (common_path === '' || common_path === '/tmp/') {
82 return file
83 }
84
85 // prepend "/" to turn relative paths to absolute
86 file.name = relative('/' + base_path, '/' + file.name)
87 return file
88 } catch (e) {
89 console.error(e)
90 // return unmodified file
91 return file
92 }
93 })
94
95 return filesWithRelPath.map((file: { name: string; content: string }, index: number) => {
96 return {
97 id: index + 1,
98 name: file.name,
99 content: file.content,
100 state: 'unchanged',
101 } as FileData
102 })
103 }
104
105 return []
106}