Skip to content

Commit 2e06694

Browse files
committed
feat(errors): add more verbose InternalCliError class with stack trace
1 parent 6a633c1 commit 2e06694

File tree

4 files changed

+80
-36
lines changed

4 files changed

+80
-36
lines changed

src/detectors/utils/jsdetect.js

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
*/
66
const { existsSync, readFileSync } = require('fs')
77

8+
const { InternalCliError } = require('../../utils/error')
9+
const { NETLIFYDEVWARN } = require('../../utils/logo')
10+
811
let pkgJSON = null
912
let yarnExists = false
1013
let warnedAboutEmptyScript = false
11-
const { NETLIFYDEVWARN } = require('../../utils/logo')
1214

1315
/** hold package.json in a singleton so we dont do expensive parsing repeatedly */
1416
const getPkgJSON = function () {
@@ -28,9 +30,7 @@ const getYarnOrNPMCommand = function () {
2830

2931
/**
3032
* real utiltiies are down here
31-
*
3233
*/
33-
3434
const hasRequiredDeps = function (requiredDepArray) {
3535
const { dependencies, devDependencies } = getPkgJSON()
3636
for (const depName of requiredDepArray) {
@@ -53,9 +53,9 @@ const hasRequiredFiles = function (filenameArr) {
5353

5454
// preferredScriptsArr is in decreasing order of preference
5555
const scanScripts = function ({ preferredScriptsArr, preferredCommand }) {
56-
const { scripts } = getPkgJSON()
56+
const packageJsonScripts = getPkgJSON().scripts
5757

58-
if (!scripts && !warnedAboutEmptyScript) {
58+
if (!packageJsonScripts && !warnedAboutEmptyScript) {
5959
console.log(`${NETLIFYDEVWARN} You have a package.json without any npm scripts.`)
6060
console.log(
6161
`${NETLIFYDEVWARN} Netlify Dev's detector system works best with a script, or you can specify a command to run in the netlify.toml [dev] block `,
@@ -65,26 +65,33 @@ const scanScripts = function ({ preferredScriptsArr, preferredCommand }) {
6565
// not going to match any scripts anyway
6666
return []
6767
}
68-
//
69-
//
70-
// NOTE: we return an array of arrays (args)
71-
// because we may want to supply extra args in some setups
72-
//
73-
// e.g. ['eleventy', '--serve', '--watch']
74-
//
75-
// array will in future be sorted by likelihood of what we want
76-
//
77-
//
78-
// this is very simplistic logic, we can offer far more intelligent logic later
79-
// eg make a dependency tree of npm scripts and offer the parentest node first
80-
return Object.entries(scripts)
81-
.filter(
82-
([scriptName, scriptCommand]) =>
83-
(preferredScriptsArr.includes(scriptName) || scriptCommand.includes(preferredCommand)) &&
84-
// prevent netlify dev calling netlify dev
85-
!scriptCommand.includes('netlify dev'),
86-
)
87-
.map(([scriptName]) => [scriptName])
68+
/**
69+
* NOTE: we return an array of arrays (args) because we may want to supply
70+
* extra args in some setups, e.g.
71+
*
72+
* ['eleventy', '--serve', '--watch']
73+
*
74+
* array will be sorted by likelihood of what we want in the future. this is
75+
* very simplistic logic, we can offer far more intelligent logic later, e.g.
76+
* make a dependency tree of npm scripts and offer the parentest node first
77+
*/
78+
const matchedScripts = []
79+
for (const [scriptName, scriptCommand] of Object.entries(packageJsonScripts)) {
80+
/**
81+
* Throw if trying to call Netlify dev from within Netlify dev. Include
82+
* detailed information about the CLI setup in the error text.
83+
*/
84+
if (scriptCommand.includes('netlify dev')) {
85+
throw new InternalCliError('Cannot call `netlify dev` inside `netlify dev`.', { packageJsonScripts })
86+
}
87+
/**
88+
* Otherwise, push the match.
89+
*/
90+
if (preferredScriptsArr.includes(scriptName) || scriptCommand.includes(preferredCommand)) {
91+
matchedScripts.push([scriptName])
92+
}
93+
}
94+
return matchedScripts
8895
}
8996

9097
module.exports = {

src/utils/detect-server.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const getPort = require('get-port')
88
const inquirer = require('inquirer')
99
const inquirerAutocompletePrompt = require('inquirer-autocomplete-prompt')
1010

11+
const { InternalCliError } = require('./error')
1112
const { NETLIFYDEVLOG, NETLIFYDEVWARN } = require('./logo')
1213

1314
const serverSettings = async (devConfig, flags, projectDir, log) => {
@@ -222,20 +223,29 @@ const loadDetector = function (detectorName) {
222223
}
223224
}
224225

226+
/**
227+
* Get the default args from an array of possible args.
228+
*
229+
* @param {Array<Array>?} possibleArgsArrs
230+
* @returns {Array}
231+
*/
225232
const chooseDefaultArgs = function (possibleArgsArrs) {
226-
// vast majority of projects will only have one matching detector
227-
// just pick the first one
233+
/**
234+
* Select first set of possible args.
235+
*/
228236
const [args] = possibleArgsArrs
229237
if (!args) {
230-
const { scripts } = JSON.parse(fs.readFileSync('package.json', { encoding: 'utf8' }))
231-
const err = new Error(
232-
'Empty args assigned, this is an internal Netlify Dev bug, please report your settings and scripts so we can improve',
233-
)
234-
err.scripts = scripts
235-
err.possibleArgsArrs = possibleArgsArrs
236-
throw err
238+
/**
239+
* Load scripts from package.json (if it exists) to display them in the
240+
* error. Initialize `scripts` to `null` so it is not omitted by
241+
* JSON.stringify() in the case it isn't reassigned.
242+
*/
243+
let packageJsonScripts = null
244+
if (fs.existsSync('package.json')) {
245+
packageJsonScripts = JSON.parse(fs.readFileSync('package.json')).scripts
246+
}
247+
throw new InternalCliError('No possible args found.', { packageJsonScripts, possibleArgsArrs })
237248
}
238-
239249
return args
240250
}
241251

src/utils/detect-server.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ test('serverSettings: no config', async (t) => {
188188
t.is(settings.noCmd, true)
189189
})
190190

191-
test('chooseDefaultArgs', (t) => {
191+
test('chooseDefaultArgs: select first from possibleArgsArrs', (t) => {
192192
const possibleArgsArrs = [['run', 'dev'], ['run develop']]
193193
const args = chooseDefaultArgs(possibleArgsArrs)
194194
t.deepEqual(args, possibleArgsArrs[0])

src/utils/error.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* An unrecoverable internal CLI error which should be reported.
3+
*/
4+
class InternalCliError extends Error {
5+
/**
6+
* Log a stack trace and the given context object for opening an issue, then
7+
* throw.
8+
*
9+
* @param {string!} message
10+
* @param {Object!} context
11+
*/
12+
constructor(message, context) {
13+
super(message)
14+
this.name = 'InternalCliError'
15+
16+
console.trace(
17+
`INTERNAL CLI ERROR. ${message}\n` +
18+
'Please open an issue at https://github.com/netlify/cli/issues/new ' +
19+
'and include the following information:' +
20+
`\n${JSON.stringify(context, null, 2)}\n`,
21+
)
22+
}
23+
}
24+
25+
module.exports = {
26+
InternalCliError,
27+
}

0 commit comments

Comments
 (0)