-
Notifications
You must be signed in to change notification settings - Fork 86
/
Copy pathanalysis.ts
137 lines (123 loc) · 3.95 KB
/
analysis.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
137
import fs, { existsSync } from 'fs'
import { relative } from 'pathe'
// I have no idea what eslint is up to here but it gives an error
// eslint-disable-next-line no-shadow
export const enum ApiRouteType {
SCHEDULED = 'experimental-scheduled',
BACKGROUND = 'experimental-background',
}
export interface ApiStandardConfig {
type?: never
runtime?: 'nodejs' | 'experimental-edge' | 'edge'
schedule?: never
}
export interface ApiScheduledConfig {
type: ApiRouteType.SCHEDULED
runtime?: 'nodejs'
schedule: string
}
export interface ApiBackgroundConfig {
type: ApiRouteType.BACKGROUND
runtime?: 'nodejs'
schedule?: never
}
export type ApiConfig = ApiStandardConfig | ApiScheduledConfig | ApiBackgroundConfig
export const isEdgeConfig = (config: string) => ['experimental-edge', 'edge'].includes(config)
export const validateConfigValue = (config: ApiConfig, apiFilePath: string): config is ApiConfig => {
if (config.type === ApiRouteType.SCHEDULED) {
if (!config.schedule) {
console.error(
`Invalid config value in ${relative(process.cwd(), apiFilePath)}: schedule is required when type is "${
ApiRouteType.SCHEDULED
}"`,
)
return false
}
if (isEdgeConfig((config as ApiConfig).runtime)) {
console.error(
`Invalid config value in ${relative(
process.cwd(),
apiFilePath,
)}: edge runtime is not supported for scheduled functions`,
)
return false
}
return true
}
if (!config.type || config.type === ApiRouteType.BACKGROUND) {
if (config.schedule) {
console.error(
`Invalid config value in ${relative(process.cwd(), apiFilePath)}: schedule is not allowed unless type is "${
ApiRouteType.SCHEDULED
}"`,
)
return false
}
if (config.type && isEdgeConfig((config as ApiConfig).runtime)) {
console.error(
`Invalid config value in ${relative(
process.cwd(),
apiFilePath,
)}: edge runtime is not supported for background functions`,
)
return false
}
return true
}
console.error(
`Invalid config value in ${relative(process.cwd(), apiFilePath)}: type ${
(config as ApiConfig).type
} is not supported`,
)
return false
}
let extractConstValue
let parseModule
let hasWarnedAboutNextVersion = false
/**
* Uses Next's swc static analysis to extract the config values from a file.
*/
export const extractConfigFromFile = async (apiFilePath: string): Promise<ApiConfig> => {
if (!apiFilePath || !existsSync(apiFilePath)) {
return {}
}
try {
if (!extractConstValue) {
extractConstValue = require('next/dist/build/analysis/extract-const-value')
}
if (!parseModule) {
// eslint-disable-next-line prefer-destructuring, @typescript-eslint/no-var-requires
parseModule = require('next/dist/build/analysis/parse-module').parseModule
}
} catch (error) {
if (error.code === 'MODULE_NOT_FOUND') {
if (!hasWarnedAboutNextVersion) {
console.log("This version of Next.js doesn't support advanced API routes. Skipping...")
hasWarnedAboutNextVersion = true
}
// Old Next.js version
return {}
}
throw error
}
const { extractExportedConstValue, UnsupportedValueError } = extractConstValue
const fileContent = await fs.promises.readFile(apiFilePath, 'utf8')
// No need to parse if there's no "config"
if (!fileContent.includes('config')) {
return {}
}
const ast = await parseModule(apiFilePath, fileContent)
let config: ApiConfig
try {
config = extractExportedConstValue(ast, 'config')
} catch (error) {
if (UnsupportedValueError && error instanceof UnsupportedValueError) {
console.warn(`Unsupported config value in ${relative(process.cwd(), apiFilePath)}`)
}
return {}
}
if (validateConfigValue(config, apiFilePath)) {
return config
}
throw new Error(`Unsupported config value in ${relative(process.cwd(), apiFilePath)}`)
}