netlify-cli 15.9.0 → 15.9.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/npm-shrinkwrap.json +749 -147
  2. package/package.json +6 -7
  3. package/scripts/postinstall.mjs +8 -8
  4. package/src/commands/base-command.mjs +195 -59
  5. package/src/commands/deploy/deploy.mjs +21 -9
  6. package/src/commands/dev/dev.mjs +21 -15
  7. package/src/commands/functions/functions-build.mjs +2 -2
  8. package/src/commands/functions/functions-create.mjs +0 -3
  9. package/src/commands/functions/functions-invoke.mjs +8 -5
  10. package/src/commands/init/init.mjs +1 -1
  11. package/src/commands/link/link.mjs +5 -5
  12. package/src/commands/main.mjs +1 -1
  13. package/src/commands/serve/serve.mjs +11 -5
  14. package/src/commands/sites/sites-create-template.mjs +1 -1
  15. package/src/commands/sites/sites-create.mjs +1 -1
  16. package/src/lib/completion/generate-autocompletion.mjs +4 -4
  17. package/src/lib/edge-functions/bootstrap.mjs +1 -1
  18. package/src/lib/edge-functions/internal.mjs +5 -3
  19. package/src/lib/edge-functions/proxy.mjs +30 -5
  20. package/src/lib/functions/registry.mjs +1 -3
  21. package/src/lib/functions/runtimes/js/builders/zisi.mjs +3 -8
  22. package/src/lib/functions/server.mjs +6 -6
  23. package/src/lib/spinner.mjs +1 -1
  24. package/src/recipes/vscode/index.mjs +1 -1
  25. package/src/utils/build-info.mjs +19 -0
  26. package/src/utils/command-helpers.mjs +16 -7
  27. package/src/utils/deploy/hash-fns.mjs +1 -2
  28. package/src/utils/detect-server-settings.mjs +148 -200
  29. package/src/utils/execa.mjs +4 -2
  30. package/src/utils/framework-server.mjs +2 -2
  31. package/src/utils/functions/functions.mjs +7 -0
  32. package/src/utils/functions/get-functions.mjs +2 -2
  33. package/src/utils/get-repo-data.mjs +5 -6
  34. package/src/utils/init/config-github.mjs +2 -2
  35. package/src/utils/init/config-manual.mjs +24 -7
  36. package/src/utils/init/utils.mjs +62 -63
  37. package/src/utils/proxy-server.mjs +7 -4
  38. package/src/utils/proxy.mjs +4 -0
  39. package/src/utils/run-build.mjs +58 -7
  40. package/src/utils/shell.mjs +14 -3
  41. package/src/utils/state-config.mjs +5 -1
  42. package/src/utils/static-server.mjs +4 -0
  43. package/src/utils/telemetry/report-error.mjs +8 -4
  44. package/src/utils/init/frameworks.mjs +0 -23
@@ -5,7 +5,12 @@ import { exit, log } from '../command-helpers.mjs'
5
5
 
6
6
  import { createDeployKey, getBuildSettings, saveNetlifyToml, setupSite } from './utils.mjs'
7
7
 
8
- const addDeployKey = async ({ deployKey }) => {
8
+ /**
9
+ * Prompts for granting the netlify ssh public key access to your repo
10
+ * @param {object} deployKey
11
+ * @param {string} deployKey.public_key
12
+ */
13
+ const addDeployKey = async (deployKey) => {
9
14
  log('\nGive this Netlify SSH public key access to your repository:\n')
10
15
  log(`\n${deployKey.public_key}\n\n`)
11
16
 
@@ -23,6 +28,11 @@ const addDeployKey = async ({ deployKey }) => {
23
28
  }
24
29
  }
25
30
 
31
+ /**
32
+ * @param {object} config
33
+ * @param {Awaited<ReturnType<import('../../utils/get-repo-data.mjs').default>>} config.repoData
34
+ * @returns {Promise<string>}
35
+ */
26
36
  const getRepoPath = async ({ repoData }) => {
27
37
  const { repoPath } = await inquirer.prompt([
28
38
  {
@@ -30,6 +40,9 @@ const getRepoPath = async ({ repoData }) => {
30
40
  name: 'repoPath',
31
41
  message: 'The SSH URL of the remote git repo:',
32
42
  default: repoData.url,
43
+ /**
44
+ * @param {string} url
45
+ */
33
46
  validate: (url) => SSH_URL_REGEXP.test(url) || 'The URL provided does not use the SSH protocol',
34
47
  },
35
48
  ])
@@ -37,7 +50,11 @@ const getRepoPath = async ({ repoData }) => {
37
50
  return repoPath
38
51
  }
39
52
 
40
- const addDeployHook = async ({ deployHook }) => {
53
+ /**
54
+ * @param {string} deployHook
55
+ * @returns
56
+ */
57
+ const addDeployHook = async (deployHook) => {
41
58
  log('\nConfigure the following webhook for your repository:\n')
42
59
  log(`\n${deployHook}\n\n`)
43
60
  const { deployHookAdded } = await inquirer.prompt([
@@ -55,14 +72,14 @@ const addDeployHook = async ({ deployHook }) => {
55
72
  /**
56
73
  * @param {object} config
57
74
  * @param {import('../../commands/base-command.mjs').default} config.command
58
- * @param {*} config.repoData
75
+ * @param {Awaited<ReturnType<import('../../utils/get-repo-data.mjs').default>>} config.repoData
59
76
  * @param {string} config.siteId
60
77
  */
61
78
  export default async function configManual({ command, repoData, siteId }) {
62
79
  const { netlify } = command
63
80
  const {
64
81
  api,
65
- cachedConfig: { configPath, env },
82
+ cachedConfig: { configPath },
66
83
  config,
67
84
  repositoryRoot,
68
85
  site: { root: siteRoot },
@@ -72,12 +89,12 @@ export default async function configManual({ command, repoData, siteId }) {
72
89
  repositoryRoot,
73
90
  siteRoot,
74
91
  config,
75
- env,
92
+ command,
76
93
  })
77
94
  await saveNetlifyToml({ repositoryRoot, config, configPath, baseDir, buildCmd, buildDir, functionsDir })
78
95
 
79
96
  const deployKey = await createDeployKey({ api })
80
- await addDeployKey({ deployKey })
97
+ await addDeployKey(deployKey)
81
98
 
82
99
  const repoPath = await getRepoPath({ repoData })
83
100
  const repo = {
@@ -99,7 +116,7 @@ export default async function configManual({ command, repoData, siteId }) {
99
116
  configPlugins: config.plugins,
100
117
  pluginsToInstall,
101
118
  })
102
- const deployHookAdded = await addDeployHook({ deployHook: updatedSite.deploy_hook })
119
+ const deployHookAdded = await addDeployHook(updatedSite.deploy_hook)
103
120
  if (!deployHookAdded) {
104
121
  exit()
105
122
  }
@@ -1,64 +1,72 @@
1
1
  // @ts-check
2
2
  import { writeFile } from 'fs/promises'
3
3
  import path from 'path'
4
- import process from 'process'
5
4
 
6
5
  import cleanDeep from 'clean-deep'
7
6
  import inquirer from 'inquirer'
8
7
 
9
8
  import { fileExistsAsync } from '../../lib/fs.mjs'
10
9
  import { normalizeBackslash } from '../../lib/path.mjs'
10
+ import { detectBuildSettings } from '../build-info.mjs'
11
11
  import { chalk, error as failAndExit, log, warn } from '../command-helpers.mjs'
12
12
 
13
- import { getFrameworkInfo } from './frameworks.mjs'
14
- import { detectNodeVersion } from './node-version.mjs'
15
13
  import { getRecommendPlugins, getUIPlugins } from './plugins.mjs'
16
14
 
17
- const normalizeDir = ({ baseDirectory, defaultValue, dir }) => {
18
- if (dir === undefined) {
19
- return defaultValue
20
- }
21
-
22
- const relativeDir = path.relative(baseDirectory, dir)
23
- return relativeDir || defaultValue
24
- }
25
-
26
- const getDefaultBase = ({ baseDirectory, repositoryRoot }) => {
27
- if (baseDirectory !== repositoryRoot && baseDirectory.startsWith(repositoryRoot)) {
28
- return path.relative(repositoryRoot, baseDirectory)
29
- }
30
- }
15
+ // these plugins represent runtimes that are
16
+ // expected to be "automatically" installed. Even though
17
+ // they can be installed on package/toml, we always
18
+ // want them installed in the site settings. When installed
19
+ // there our build will automatically install the latest without
20
+ // user management of the versioning.
21
+ const pluginsToAlwaysInstall = new Set(['@netlify/plugin-nextjs'])
22
+
23
+ /**
24
+ * Retrieve a list of plugins to auto install
25
+ * @param {string[]=} pluginsInstalled
26
+ * @param {string[]=} pluginsRecommended
27
+ * @returns
28
+ */
29
+ export const getPluginsToAutoInstall = (pluginsInstalled = [], pluginsRecommended = []) =>
30
+ pluginsRecommended.reduce(
31
+ (acc, plugin) =>
32
+ pluginsInstalled.includes(plugin) && !pluginsToAlwaysInstall.has(plugin) ? acc : [...acc, plugin],
33
+
34
+ /** @type {string[]} */ ([]),
35
+ )
31
36
 
32
- const getDefaultSettings = ({
33
- baseDirectory,
34
- config,
35
- frameworkBuildCommand,
36
- frameworkBuildDir,
37
- frameworkPlugins,
38
- repositoryRoot,
39
- }) => {
40
- const recommendedPlugins = getRecommendPlugins(frameworkPlugins, config)
41
- const {
42
- command: defaultBuildCmd = frameworkBuildCommand,
43
- functions: defaultFunctionsDir,
44
- publish: defaultBuildDir = frameworkBuildDir,
45
- } = config.build
37
+ /**
38
+ *
39
+ * @param {Partial<import('@netlify/build-info').Settings>} settings
40
+ * @param {*} config
41
+ * @param {import('../../commands/base-command.mjs').default} command
42
+ */
43
+ const normalizeSettings = (settings, config, command) => {
44
+ const plugins = getPluginsToAutoInstall(settings.plugins_from_config_file, settings.plugins_recommended)
45
+ const recommendedPlugins = getRecommendPlugins(plugins, config)
46
46
 
47
47
  return {
48
- defaultBaseDir: getDefaultBase({ repositoryRoot, baseDirectory }),
49
- defaultBuildCmd,
50
- defaultBuildDir: normalizeDir({ baseDirectory, dir: defaultBuildDir, defaultValue: '.' }),
51
- defaultFunctionsDir: normalizeDir({ baseDirectory, dir: defaultFunctionsDir, defaultValue: 'netlify/functions' }),
48
+ defaultBaseDir: settings.baseDirectory ?? command.project.relativeBaseDirectory ?? '',
49
+ defaultBuildCmd: config.build.command || settings.buildCommand,
50
+ defaultBuildDir: settings.dist,
51
+ defaultFunctionsDir: config.build.functions || 'netlify/functions',
52
52
  recommendedPlugins,
53
53
  }
54
54
  }
55
55
 
56
+ /**
57
+ *
58
+ * @param {object} param0
59
+ * @param {string} param0.defaultBaseDir
60
+ * @param {string} param0.defaultBuildCmd
61
+ * @param {string=} param0.defaultBuildDir
62
+ * @returns
63
+ */
56
64
  const getPromptInputs = ({ defaultBaseDir, defaultBuildCmd, defaultBuildDir }) => {
57
65
  const inputs = [
58
66
  defaultBaseDir !== undefined && {
59
67
  type: 'input',
60
68
  name: 'baseDir',
61
- message: 'Base directory (e.g. projects/frontend):',
69
+ message: 'Base directory `(blank for current dir):',
62
70
  default: defaultBaseDir,
63
71
  },
64
72
  {
@@ -79,34 +87,22 @@ const getPromptInputs = ({ defaultBaseDir, defaultBuildCmd, defaultBuildDir }) =
79
87
  return inputs.filter(Boolean)
80
88
  }
81
89
 
82
- // `repositoryRoot === siteRoot` means the base directory wasn't detected by @netlify/config, so we use cwd()
83
- const getBaseDirectory = ({ repositoryRoot, siteRoot }) =>
84
- path.normalize(repositoryRoot) === path.normalize(siteRoot) ? process.cwd() : siteRoot
85
-
86
- export const getBuildSettings = async ({ config, env, repositoryRoot, siteRoot }) => {
87
- const baseDirectory = getBaseDirectory({ repositoryRoot, siteRoot })
88
- const nodeVersion = await detectNodeVersion({ baseDirectory, env })
89
- const {
90
- frameworkBuildCommand,
91
- frameworkBuildDir,
92
- frameworkName,
93
- frameworkPlugins = [],
94
- } = await getFrameworkInfo({
95
- baseDirectory,
96
- nodeVersion,
97
- })
90
+ /**
91
+ * @param {object} param0
92
+ * @param {*} param0.config
93
+ * @param {import('../../commands/base-command.mjs').default} param0.command
94
+ */
95
+ export const getBuildSettings = async ({ command, config }) => {
96
+ const settings = await detectBuildSettings(command)
97
+ // TODO: add prompt for asking to choose the build command
98
+ /** @type {Partial<import('@netlify/build-info').Settings>} */
99
+ // eslint-disable-next-line unicorn/explicit-length-check
100
+ const setting = settings.length > 0 ? settings[0] : {}
98
101
  const { defaultBaseDir, defaultBuildCmd, defaultBuildDir, defaultFunctionsDir, recommendedPlugins } =
99
- await getDefaultSettings({
100
- repositoryRoot,
101
- config,
102
- baseDirectory,
103
- frameworkBuildCommand,
104
- frameworkBuildDir,
105
- frameworkPlugins,
106
- })
107
-
108
- if (recommendedPlugins.length !== 0) {
109
- log(`Configuring ${formatTitle(frameworkName)} runtime...`)
102
+ await normalizeSettings(setting, config, command)
103
+
104
+ if (recommendedPlugins.length !== 0 && setting.framework?.name) {
105
+ log(`Configuring ${formatTitle(setting.framework?.name)} runtime...`)
110
106
  log()
111
107
  }
112
108
 
@@ -199,6 +195,9 @@ export const formatErrorMessage = ({ error, message }) => {
199
195
  return `${message} with error: ${chalk.red(errorMessage)}`
200
196
  }
201
197
 
198
+ /**
199
+ * @param {string} title
200
+ */
202
201
  const formatTitle = (title) => chalk.cyan(title)
203
202
 
204
203
  export const createDeployKey = async ({ api }) => {
@@ -36,19 +36,21 @@ export const generateInspectSettings = (edgeInspect, edgeInspectBrk) => {
36
36
  /**
37
37
  *
38
38
  * @param {object} params
39
+ * @param {string=} params.accountId
39
40
  * @param {*} params.addonsUrls
40
- * @param {import('../commands/base-command.mjs').NetlifyOptions["config"]} params.config
41
+ * @param {import('../commands/types.js').NetlifyOptions["config"]} params.config
41
42
  * @param {string} [params.configPath] An override for the Netlify config path
42
43
  * @param {boolean} params.debug
43
- * @param {import('../commands/base-command.mjs').NetlifyOptions["cachedConfig"]['env']} params.env
44
+ * @param {import('../commands/types.js').NetlifyOptions["cachedConfig"]['env']} params.env
44
45
  * @param {InspectSettings} params.inspectSettings
45
46
  * @param {() => Promise<object>} params.getUpdatedConfig
46
47
  * @param {string} params.geolocationMode
47
48
  * @param {string} params.geoCountry
48
49
  * @param {*} params.settings
49
50
  * @param {boolean} params.offline
50
- * @param {*} params.site
51
+ * @param {object} params.site
51
52
  * @param {*} params.siteInfo
53
+ * @param {string} params.projectDir
52
54
  * @param {import('./state-config.mjs').default} params.state
53
55
  * @returns
54
56
  */
@@ -64,6 +66,7 @@ export const startProxyServer = async ({
64
66
  getUpdatedConfig,
65
67
  inspectSettings,
66
68
  offline,
69
+ projectDir,
67
70
  settings,
68
71
  site,
69
72
  siteInfo,
@@ -80,7 +83,7 @@ export const startProxyServer = async ({
80
83
  getUpdatedConfig,
81
84
  inspectSettings,
82
85
  offline,
83
- projectDir: site.root,
86
+ projectDir,
84
87
  settings,
85
88
  state,
86
89
  siteInfo,
@@ -599,6 +599,10 @@ const onRequest = async (
599
599
  proxy.web(req, res, options)
600
600
  }
601
601
 
602
+ /**
603
+ * @param {import('./types.js').ServerSettings} settings
604
+ * @returns
605
+ */
602
606
  export const getProxyUrl = function (settings) {
603
607
  const scheme = settings.https ? 'https' : 'http'
604
608
  return `${scheme}://localhost:${settings.port}`
@@ -1,7 +1,6 @@
1
1
  // @ts-check
2
2
  import { promises as fs } from 'fs'
3
3
  import path from 'path'
4
- import process from 'process'
5
4
 
6
5
  import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from '../lib/edge-functions/consts.mjs'
7
6
  import { getPathInProject } from '../lib/settings.mjs'
@@ -12,8 +11,13 @@ import { INTERNAL_FUNCTIONS_FOLDER } from './functions/index.mjs'
12
11
 
13
12
  const netlifyBuildPromise = import('@netlify/build')
14
13
 
15
- // Copies `netlify.toml`, if one is defined, into the `.netlify` internal
16
- // directory and returns the path to its new location.
14
+ /**
15
+ * Copies `netlify.toml`, if one is defined, into the `.netlify` internal
16
+ * directory and returns the path to its new location.
17
+ * @param {object} config
18
+ * @param {string} config.configPath
19
+ * @param {string} config.siteRoot
20
+ */
17
21
  const copyConfig = async ({ configPath, siteRoot }) => {
18
22
  const newConfigPath = path.resolve(siteRoot, getPathInProject(['netlify.toml']))
19
23
 
@@ -26,6 +30,9 @@ const copyConfig = async ({ configPath, siteRoot }) => {
26
30
  return newConfigPath
27
31
  }
28
32
 
33
+ /**
34
+ * @param {string} basePath
35
+ */
29
36
  const cleanInternalDirectory = async (basePath) => {
30
37
  const ops = [INTERNAL_FUNCTIONS_FOLDER, INTERNAL_EDGE_FUNCTIONS_FOLDER, 'netlify.toml'].map((name) => {
31
38
  const fullPath = path.resolve(basePath, getPathInProject([name]))
@@ -36,9 +43,26 @@ const cleanInternalDirectory = async (basePath) => {
36
43
  await Promise.all(ops)
37
44
  }
38
45
 
46
+ /**
47
+ *
48
+ * @param {object} config
49
+ * @param {string} config.projectDir
50
+ * @param {*} config.cachedConfig
51
+ * @param {object} config.options
52
+ * @param {string} config.options.configPath
53
+ * @param {*} config.options.context
54
+ * @param {string=} config.options.cwd
55
+ * @param {boolean} config.options.debug
56
+ * @param {boolean} config.options.dry
57
+ * @param {boolean} config.options.offline
58
+ * @param {boolean} config.options.quiet
59
+ * @param {boolean} config.options.saveConfig
60
+ * @returns
61
+ */
39
62
  const getBuildOptions = ({
40
63
  cachedConfig,
41
- options: { configPath, context, cwd = process.cwd(), debug, dry, offline, quiet, saveConfig },
64
+ options: { configPath, context, debug, dry, offline, quiet, saveConfig },
65
+ projectDir,
42
66
  }) => ({
43
67
  cachedConfig,
44
68
  configPath,
@@ -51,14 +75,35 @@ const getBuildOptions = ({
51
75
  telemetry: false,
52
76
  buffer: false,
53
77
  offline,
54
- cwd,
78
+ cwd: projectDir,
55
79
  quiet,
56
80
  saveConfig,
57
81
  })
58
82
 
59
- const runNetlifyBuild = async ({ cachedConfig, env, options, settings, site, timeline = 'build' }) => {
83
+ /**
84
+ *
85
+ * @param {object} config
86
+ * @param {*} config.cachedConfig
87
+ * @param {NodeJS.ProcessEnv} config.env
88
+ * @param {*} config.options The flags of the command
89
+ * @param {string} config.projectDir
90
+ * @param {import('./types.js').ServerSettings} config.settings
91
+ * @param {*} config.site
92
+ * @param {'build' | 'dev'} config.timeline
93
+ * @returns
94
+ */
95
+ export const runNetlifyBuild = async ({
96
+ cachedConfig,
97
+ env,
98
+ options,
99
+ projectDir,
100
+ settings,
101
+ site,
102
+ timeline = 'build',
103
+ }) => {
60
104
  const { default: buildSite, startDev } = await netlifyBuildPromise
61
105
  const sharedOptions = getBuildOptions({
106
+ projectDir,
62
107
  cachedConfig,
63
108
  options,
64
109
  })
@@ -118,13 +163,19 @@ const runNetlifyBuild = async ({ cachedConfig, env, options, settings, site, tim
118
163
  // Run Netlify Build using the `startDev` entry point.
119
164
  const { error: startDevError, success } = await startDev(devCommand, startDevOptions)
120
165
 
121
- if (!success) {
166
+ if (!success && startDevError) {
122
167
  error(`Could not start local development server\n\n${startDevError.message}\n\n${startDevError.stack}`)
123
168
  }
124
169
 
125
170
  return {}
126
171
  }
127
172
 
173
+ /**
174
+ * @param {Omit<Parameters<typeof runNetlifyBuild>[0], 'timeline'>} options
175
+ */
128
176
  export const runDevTimeline = (options) => runNetlifyBuild({ ...options, timeline: 'dev' })
129
177
 
178
+ /**
179
+ * @param {Omit<Parameters<typeof runNetlifyBuild>[0], 'timeline'>} options
180
+ */
130
181
  export const runBuildTimeline = (options) => runNetlifyBuild({ ...options, timeline: 'build' })
@@ -48,7 +48,11 @@ export const runCommand = (command, env = {}, spinner = null) => {
48
48
  preferLocal: true,
49
49
  // we use reject=false to avoid rejecting synchronously when the command doesn't exist
50
50
  reject: false,
51
- env,
51
+ env: {
52
+ // we want always colorful terminal outputs
53
+ FORCE_COLOR: 'true',
54
+ ...env,
55
+ },
52
56
  // windowsHide needs to be false for child process to terminate properly on Windows
53
57
  windowsHide: false,
54
58
  })
@@ -90,7 +94,7 @@ export const runCommand = (command, env = {}, spinner = null) => {
90
94
  ? `${NETLIFYDEVERR} ${result.shortMessage}`
91
95
  : `${NETLIFYDEVWARN} "${command}" exited with code ${result.exitCode}`
92
96
 
93
- log(`${errorMessage}. Shutting down Netlify Dev server`)
97
+ log(`\n\n${errorMessage}. Shutting down Netlify Dev server`)
94
98
  }
95
99
 
96
100
  return await cleanupBeforeExit({ exitCode: 1 })
@@ -100,6 +104,13 @@ export const runCommand = (command, env = {}, spinner = null) => {
100
104
  return commandProcess
101
105
  }
102
106
 
107
+ /**
108
+ *
109
+ * @param {object} config
110
+ * @param {string} config.command
111
+ * @param {*} config.error
112
+ * @returns
113
+ */
103
114
  const isNonExistingCommandError = ({ command, error: commandError }) => {
104
115
  // `ENOENT` is only returned for non Windows systems
105
116
  // See https://github.com/sindresorhus/execa/pull/447
@@ -108,7 +119,7 @@ const isNonExistingCommandError = ({ command, error: commandError }) => {
108
119
  }
109
120
 
110
121
  // if the command is a package manager we let it report the error
111
- if (['yarn', 'npm'].includes(command)) {
122
+ if (['yarn', 'npm', 'pnpm'].includes(command)) {
112
123
  return false
113
124
  }
114
125
 
@@ -11,7 +11,11 @@ import { getPathInProject } from '../lib/settings.mjs'
11
11
  const STATE_PATH = getPathInProject(['state.json'])
12
12
  const permissionError = "You don't have access to this file."
13
13
 
14
- // Finds location of `.netlify/state.json`
14
+ /**
15
+ * Finds location of `.netlify/state.json`
16
+ * @param {string} cwd
17
+ * @returns {string}
18
+ */
15
19
  const findStatePath = (cwd) => {
16
20
  const statePath = findUpSync([STATE_PATH], { cwd })
17
21
 
@@ -6,6 +6,10 @@ import Fastify from 'fastify'
6
6
 
7
7
  import { log, NETLIFYDEVLOG } from './command-helpers.mjs'
8
8
 
9
+ /**
10
+ * @param {object} config
11
+ * @param {import('./types.js').ServerSettings} config.settings
12
+ */
9
13
  export const startStaticServer = async ({ settings }) => {
10
14
  const server = Fastify()
11
15
  const rootPath = path.resolve(settings.dist)
@@ -25,15 +25,19 @@ export const reportError = async function (error, config = {}) {
25
25
  return
26
26
  }
27
27
 
28
+ // convert a NotifiableError to an error class
29
+ // eslint-disable-next-line unicorn/no-nested-ternary
30
+ const err = error instanceof Error ? error : typeof error === 'string' ? new Error(error) : error
31
+
28
32
  const globalConfig = await getGlobalConfig()
29
33
 
30
34
  const options = JSON.stringify({
31
35
  type: 'error',
32
36
  data: {
33
- message: error.message,
34
- name: error.name,
35
- stack: error.stack,
36
- cause: error.cause,
37
+ message: err.message,
38
+ name: err.name,
39
+ stack: err.stack,
40
+ cause: err.cause,
37
41
  severity: config.severity,
38
42
  user: {
39
43
  id: globalConfig.get('userId'),
@@ -1,23 +0,0 @@
1
- // @ts-check
2
- import { listFrameworks } from '@netlify/framework-info'
3
-
4
- export const getFrameworkInfo = async ({ baseDirectory, nodeVersion }) => {
5
- const frameworks = await listFrameworks({ projectDir: baseDirectory, nodeVersion })
6
- // several frameworks can be detected - first one has highest priority
7
- if (frameworks.length !== 0) {
8
- const [
9
- {
10
- build: { commands, directory },
11
- name,
12
- plugins,
13
- },
14
- ] = frameworks
15
- return {
16
- frameworkName: name,
17
- frameworkBuildCommand: commands[0],
18
- frameworkBuildDir: directory,
19
- frameworkPlugins: plugins,
20
- }
21
- }
22
- return {}
23
- }