forked from netlify/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdetect-server.js
More file actions
247 lines (221 loc) · 9.34 KB
/
Copy pathdetect-server.js
File metadata and controls
247 lines (221 loc) · 9.34 KB
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
const path = require('path')
const chalk = require('chalk')
const getPort = require('get-port')
const { NETLIFYDEVLOG, NETLIFYDEVWARN } = require('./logo')
const inquirer = require('inquirer')
const fuzzy = require('fuzzy')
const fs = require('fs')
module.exports.serverSettings = async (devConfig, flags, projectDir, log) => {
let settings = {}
const detectorsFiles = fs.readdirSync(path.join(__dirname, '..', 'detectors')).filter(x => x.endsWith('.js')) // only accept .js detector files
if (typeof devConfig.framework !== 'string') throw new Error('Invalid "framework" option provided in config')
if (flags.dir) {
settings = await getStaticServerSettings(settings, flags, projectDir, log)
;['command', 'targetPort'].forEach(p => {
if (flags[p]) {
throw new Error(
`"${p}" option cannot be used in conjunction with "dir" flag which is used to run a static server`
)
}
})
} else if (devConfig.framework === '#auto' && !(devConfig.command && devConfig.targetPort)) {
const settingsArr = []
const detectors = detectorsFiles.map(det => {
try {
return loadDetector(det)
} catch (err) {
console.error(err)
return null
}
})
for (const detector of detectors) {
const detectorResult = detector(projectDir)
if (detectorResult) settingsArr.push(detectorResult)
}
if (settingsArr.length === 1) {
settings = settingsArr[0]
settings.args = chooseDefaultArgs(settings.possibleArgsArrs)
} else if (settingsArr.length > 1) {
/** multiple matching detectors, make the user choose */
// lazy loading on purpose
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
const scriptInquirerOptions = formatSettingsArrForInquirer(settingsArr)
const { chosenSetting } = await inquirer.prompt({
name: 'chosenSetting',
message: `Multiple possible start commands found`,
type: 'autocomplete',
async source(_, input) {
if (!input || input === '') {
return scriptInquirerOptions
}
// only show filtered results
return filterSettings(scriptInquirerOptions, input)
},
})
settings = chosenSetting // finally! we have a selected option
log(
`Add \`framework = "${chosenSetting.framework}"\` to [dev] section of your netlify.toml to avoid this selection prompt next time`
)
}
} else if (devConfig.framework === '#custom' || (devConfig.command && devConfig.targetPort)) {
settings.framework = '#custom'
if (
devConfig.framework &&
!['command', 'targetPort'].every(p => Object.prototype.hasOwnProperty.call(devConfig, p))
) {
throw new Error('"command" and "targetPort" properties are required when "framework" is set to "#custom"')
}
if (devConfig.framework !== '#custom' && devConfig.command && devConfig.targetPort) {
throw new Error(
'"framework" option must be set to "#custom" when specifying both "command" and "targetPort" options'
)
}
} else if (devConfig.framework === '#static') {
// Do nothing
} else {
const detectorName = detectorsFiles.find(dt => dt === `${devConfig.framework}.js`)
if (!detectorName)
throw new Error(
'Unsupported value provided for "framework" option in config. Please use "#custom"' +
` if you're using a framework not intrinsically supported by Netlify Dev. E.g. with "command" and "targetPort" options.` +
` Or use one of following values: ${detectorsFiles.map(f => `"${path.parse(f).name}"`).join(', ')}`
)
const detector = loadDetector(detectorName)
const detectorResult = detector(projectDir)
if (!detectorResult)
throw new Error(
`Specified "framework" detector "${devConfig.framework}" did not pass requirements for your project`
)
settings = detectorResult
settings.args = chooseDefaultArgs(detectorResult.possibleArgsArrs)
}
if (settings.command === 'npm' && !['start', 'run'].includes(settings.args[0])) {
settings.args.unshift('run')
}
if (!settings.noCmd && devConfig.command) {
console.log(
`${NETLIFYDEVLOG} Overriding ${chalk.yellow('command')} with setting derived from netlify.toml [dev] block: `,
devConfig.command
)
const [devConfigCommand, ...devConfigArgs] = devConfig.command.split(/\s+/)
settings.command = devConfigCommand
settings.args = devConfigArgs
}
settings.dist = flags.dir || devConfig.publish || settings.dist
if (devConfig.targetPort) {
if (devConfig.targetPort && typeof devConfig.targetPort !== 'number') {
throw new Error('Invalid "targetPort" option specified. The value of "targetPort" option must be an integer')
}
if (devConfig.targetPort === devConfig.port) {
throw new Error(
'"port" and "targetPort" options cannot have same values. Please consult the documentation for more details: https://cli.netlify.com/netlify-dev#netlifytoml-dev-block'
)
}
if (!settings.command)
throw new Error(
'No "command" specified or detected. The "command" option is required to use "targetPort" option.'
)
if (flags.dir)
throw new Error(
'"targetPort" option cannot be used in conjunction with "dir" flag which is used to run a static server.'
)
settings.frameworkPort = devConfig.targetPort
}
if (devConfig.port && devConfig.port === settings.frameworkPort) {
throw new Error(
'The "port" option you specified conflicts with the port of your application. Please use a different value for "port"'
)
}
if (!settings.command && !settings.framework && !settings.noCmd) {
settings = await getStaticServerSettings(settings, flags, projectDir, log)
}
if (!settings.frameworkPort) throw new Error('No "targetPort" option specified or detected.')
if (devConfig.port && typeof devConfig.port !== 'number') {
throw new Error('Invalid "port" option specified. The value of "port" option must be an integer')
}
if (devConfig.port && devConfig.port === settings.frameworkPort) {
throw new Error(
'The "port" option you specified conflicts with the port of your application. Please use a different value for "port"'
)
}
const triedPort = devConfig.port || DEFAULT_PORT
settings.port = await getPort({ port: triedPort })
if (triedPort !== settings.port && devConfig.port) {
throw new Error(`Could not acquire required "port": ${triedPort}`)
}
settings.jwtRolePath = devConfig.jwtRolePath || 'app_metadata.authorization.roles'
settings.functionsPort = await getPort({ port: settings.functionsPort || 0 })
settings.functions = devConfig.functions || settings.functions
return settings
}
const DEFAULT_PORT = 8888
async function getStaticServerSettings(settings, flags, projectDir, log) {
let dist = settings.dist
if (flags.dir) {
log(`${NETLIFYDEVWARN} Using simple static server because --dir flag was specified`)
dist = flags.dir
} else {
log(`${NETLIFYDEVWARN} No app server detected and no "command" specified`)
}
if (!dist) {
log(`${NETLIFYDEVLOG} Using current working directory`)
log(`${NETLIFYDEVWARN} Unable to determine public folder to serve files from`)
log(`${NETLIFYDEVWARN} Setup a netlify.toml file with a [dev] section to specify your dev server settings.`)
log(`${NETLIFYDEVWARN} See docs at: https://cli.netlify.com/netlify-dev#project-detection`)
dist = process.cwd()
}
log(`${NETLIFYDEVWARN} Running static server from "${path.relative(path.dirname(projectDir), dist)}"`)
return {
noCmd: true,
frameworkPort: await getPort({ port: flags.staticServerPort || 3999 }),
dist,
}
}
function loadDetector(detectorName) {
try {
return require(path.join(__dirname, '..', 'detectors', detectorName))
} catch (err) {
throw new Error(
`Failed to load detector: ${chalk.yellow(
detectorName
)}, this is likely a bug in the detector, please file an issue in netlify-cli\n ${err}`
)
}
}
module.exports.loadDetector = loadDetector
function chooseDefaultArgs(possibleArgsArrs) {
// vast majority of projects will only have one matching detector
const args = possibleArgsArrs[0] // just pick the first one
if (!args) {
const { scripts } = JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' }))
const err = new Error(
'Empty args assigned, this is an internal Netlify Dev bug, please report your settings and scripts so we can improve'
)
err.scripts = scripts
err.possibleArgsArrs = possibleArgsArrs
throw err
}
return args
}
module.exports.chooseDefaultArgs = chooseDefaultArgs
/** utilities for the inquirer section above */
function filterSettings(scriptInquirerOptions, input) {
const filteredSettings = fuzzy.filter(
input,
scriptInquirerOptions.map(x => x.name)
)
const filteredSettingNames = filteredSettings.map(x => (input ? x.string : x))
return scriptInquirerOptions.filter(t => filteredSettingNames.includes(t.name))
}
/** utiltities for the inquirer section above */
function formatSettingsArrForInquirer(settingsArr) {
return [].concat(
...settingsArr.map(setting =>
setting.possibleArgsArrs.map(args => ({
name: `[${chalk.yellow(setting.framework)}] ${setting.command} ${args.join(' ')}`,
value: { ...setting, args },
short: setting.framework + '-' + args.join(' '),
}))
)
)
}