netlify-cli 14.1.0 → 14.2.1

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.
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
- "version": "14.1.0",
3
+ "version": "14.2.1",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "netlify-cli",
9
- "version": "14.1.0",
9
+ "version": "14.2.1",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
13
13
  "@bugsnag/js": "^7.20.0",
14
14
  "@fastify/static": "^6.6.0",
15
15
  "@netlify/build": "^29.9.2",
16
+ "@netlify/build-info": "^7.0.0-pre-20230418.0",
16
17
  "@netlify/config": "^20.3.7",
17
18
  "@netlify/edge-bundler": "^8.13.2",
18
19
  "@netlify/framework-info": "^9.8.5",
@@ -715,6 +716,85 @@
715
716
  "node": "^14.16.0 || >=16.0.0"
716
717
  }
717
718
  },
719
+ "node_modules/@netlify/build-info": {
720
+ "version": "7.0.0-pre-20230418.0",
721
+ "resolved": "https://registry.npmjs.org/@netlify/build-info/-/build-info-7.0.0-pre-20230418.0.tgz",
722
+ "integrity": "sha512-2SH071pIs6eufG5ZBFusZ2FVvpp1UKf6JDChOwNYWcQ86kaELqAkfE4fGE/5cXDEvp8ieid4HdD9i6DgwIIVxw==",
723
+ "dependencies": {
724
+ "@bugsnag/js": "^7.20.0",
725
+ "@netlify/framework-info": "^9.8.5",
726
+ "find-up": "^6.3.0",
727
+ "minimatch": "^6.2.0",
728
+ "read-pkg": "^7.1.0",
729
+ "semver": "^7.3.8",
730
+ "yaml": "^2.1.3",
731
+ "yargs": "^17.6.0"
732
+ },
733
+ "bin": {
734
+ "build-info": "bin.js"
735
+ },
736
+ "engines": {
737
+ "node": "^14.16.0 || >=16.0.0"
738
+ }
739
+ },
740
+ "node_modules/@netlify/build-info/node_modules/brace-expansion": {
741
+ "version": "2.0.1",
742
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
743
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
744
+ "dependencies": {
745
+ "balanced-match": "^1.0.0"
746
+ }
747
+ },
748
+ "node_modules/@netlify/build-info/node_modules/minimatch": {
749
+ "version": "6.2.0",
750
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz",
751
+ "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==",
752
+ "dependencies": {
753
+ "brace-expansion": "^2.0.1"
754
+ },
755
+ "engines": {
756
+ "node": ">=10"
757
+ },
758
+ "funding": {
759
+ "url": "https://github.com/sponsors/isaacs"
760
+ }
761
+ },
762
+ "node_modules/@netlify/build-info/node_modules/read-pkg": {
763
+ "version": "7.1.0",
764
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz",
765
+ "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==",
766
+ "dependencies": {
767
+ "@types/normalize-package-data": "^2.4.1",
768
+ "normalize-package-data": "^3.0.2",
769
+ "parse-json": "^5.2.0",
770
+ "type-fest": "^2.0.0"
771
+ },
772
+ "engines": {
773
+ "node": ">=12.20"
774
+ },
775
+ "funding": {
776
+ "url": "https://github.com/sponsors/sindresorhus"
777
+ }
778
+ },
779
+ "node_modules/@netlify/build-info/node_modules/type-fest": {
780
+ "version": "2.19.0",
781
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
782
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
783
+ "engines": {
784
+ "node": ">=12.20"
785
+ },
786
+ "funding": {
787
+ "url": "https://github.com/sponsors/sindresorhus"
788
+ }
789
+ },
790
+ "node_modules/@netlify/build-info/node_modules/yaml": {
791
+ "version": "2.2.1",
792
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz",
793
+ "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==",
794
+ "engines": {
795
+ "node": ">= 14"
796
+ }
797
+ },
718
798
  "node_modules/@netlify/build/node_modules/@sindresorhus/is": {
719
799
  "version": "5.3.0",
720
800
  "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.3.0.tgz",
@@ -16211,6 +16291,60 @@
16211
16291
  }
16212
16292
  }
16213
16293
  },
16294
+ "@netlify/build-info": {
16295
+ "version": "7.0.0-pre-20230418.0",
16296
+ "resolved": "https://registry.npmjs.org/@netlify/build-info/-/build-info-7.0.0-pre-20230418.0.tgz",
16297
+ "integrity": "sha512-2SH071pIs6eufG5ZBFusZ2FVvpp1UKf6JDChOwNYWcQ86kaELqAkfE4fGE/5cXDEvp8ieid4HdD9i6DgwIIVxw==",
16298
+ "requires": {
16299
+ "@bugsnag/js": "^7.20.0",
16300
+ "@netlify/framework-info": "^9.8.5",
16301
+ "find-up": "^6.3.0",
16302
+ "minimatch": "^6.2.0",
16303
+ "read-pkg": "^7.1.0",
16304
+ "semver": "^7.3.8",
16305
+ "yaml": "^2.1.3",
16306
+ "yargs": "^17.6.0"
16307
+ },
16308
+ "dependencies": {
16309
+ "brace-expansion": {
16310
+ "version": "2.0.1",
16311
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
16312
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
16313
+ "requires": {
16314
+ "balanced-match": "^1.0.0"
16315
+ }
16316
+ },
16317
+ "minimatch": {
16318
+ "version": "6.2.0",
16319
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz",
16320
+ "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==",
16321
+ "requires": {
16322
+ "brace-expansion": "^2.0.1"
16323
+ }
16324
+ },
16325
+ "read-pkg": {
16326
+ "version": "7.1.0",
16327
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-7.1.0.tgz",
16328
+ "integrity": "sha512-5iOehe+WF75IccPc30bWTbpdDQLOCc3Uu8bi3Dte3Eueij81yx1Mrufk8qBx/YAbR4uL1FdUr+7BKXDwEtisXg==",
16329
+ "requires": {
16330
+ "@types/normalize-package-data": "^2.4.1",
16331
+ "normalize-package-data": "^3.0.2",
16332
+ "parse-json": "^5.2.0",
16333
+ "type-fest": "^2.0.0"
16334
+ }
16335
+ },
16336
+ "type-fest": {
16337
+ "version": "2.19.0",
16338
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
16339
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
16340
+ },
16341
+ "yaml": {
16342
+ "version": "2.2.1",
16343
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.1.tgz",
16344
+ "integrity": "sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw=="
16345
+ }
16346
+ }
16347
+ },
16214
16348
  "@netlify/cache-utils": {
16215
16349
  "version": "5.1.3",
16216
16350
  "resolved": "https://registry.npmjs.org/@netlify/cache-utils/-/cache-utils-5.1.3.tgz",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
3
  "description": "Netlify command line tool",
4
- "version": "14.1.0",
4
+ "version": "14.2.1",
5
5
  "author": "Netlify Inc.",
6
6
  "type": "module",
7
7
  "engines": {
@@ -45,6 +45,7 @@
45
45
  "@bugsnag/js": "^7.20.0",
46
46
  "@fastify/static": "^6.6.0",
47
47
  "@netlify/build": "^29.9.2",
48
+ "@netlify/build-info": "^7.0.0-pre-20230418.0",
48
49
  "@netlify/config": "^20.3.7",
49
50
  "@netlify/edge-bundler": "^8.13.2",
50
51
  "@netlify/framework-info": "^9.8.5",
@@ -111,7 +111,12 @@ const dev = async (options, command) => {
111
111
  /** @type {Partial<import('../../utils/types').ServerSettings>} */
112
112
  let settings = {}
113
113
  try {
114
- settings = await detectServerSettings(devConfig, options, site.root)
114
+ settings = await detectServerSettings(devConfig, options, site.root, {
115
+ site: {
116
+ id: site.id,
117
+ url: siteUrl,
118
+ },
119
+ })
115
120
 
116
121
  cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
117
122
  } catch (error_) {
@@ -4,6 +4,9 @@ import { EOL } from 'os'
4
4
  import path from 'path'
5
5
  import process from 'process'
6
6
 
7
+ import { Project } from '@netlify/build-info'
8
+ // eslint-disable-next-line import/extensions, n/no-missing-import
9
+ import { NodeFS } from '@netlify/build-info/node'
7
10
  import { getFramework, listFrameworks } from '@netlify/framework-info'
8
11
  import fuzzy from 'fuzzy'
9
12
  import getPort from 'get-port'
@@ -12,6 +15,7 @@ import isPlainObject from 'is-plain-obj'
12
15
  import { NETLIFYDEVWARN, chalk, log } from './command-helpers.mjs'
13
16
  import { acquirePort } from './dev.mjs'
14
17
  import { getInternalFunctionsDir } from './functions/functions.mjs'
18
+ import { reportError } from './telemetry/report-error.mjs'
15
19
 
16
20
  const formatProperty = (str) => chalk.magenta(`'${str}'`)
17
21
  const formatValue = (str) => chalk.green(`'${str}'`)
@@ -112,10 +116,10 @@ const getStaticServerPort = async ({ devConfig }) => {
112
116
  /**
113
117
  *
114
118
  * @param {object} param0
115
- * @param {import('../commands/dev/types').DevConfig} param0.devConfig
119
+ * @param {import('../commands/dev/types.js').DevConfig} param0.devConfig
116
120
  * @param {import('commander').OptionValues} param0.options
117
121
  * @param {string} param0.projectDir
118
- * @returns {Promise<import('./types').BaseServerSettings>}
122
+ * @returns {Promise<import('./types.js').BaseServerSettings>}
119
123
  */
120
124
  const handleStaticServer = async ({ devConfig, options, projectDir }) => {
121
125
  validateNumberProperty({ devConfig, property: 'staticServerPort' })
@@ -152,8 +156,8 @@ const handleStaticServer = async ({ devConfig, options, projectDir }) => {
152
156
 
153
157
  /**
154
158
  * Retrieves the settings from a framework
155
- * @param {import('./types').FrameworkInfo} framework
156
- * @returns {import('./types').BaseServerSettings}
159
+ * @param {import('./types.js').FrameworkInfo} framework
160
+ * @returns {import('./types.js').BaseServerSettings}
157
161
  */
158
162
  const getSettingsFromFramework = (framework) => {
159
163
  const {
@@ -182,6 +186,71 @@ const getSettingsFromFramework = (framework) => {
182
186
 
183
187
  const hasDevCommand = (framework) => Array.isArray(framework.dev.commands) && framework.dev.commands.length !== 0
184
188
 
189
+ /**
190
+ * The new build setting detection with build systems and frameworks combined
191
+ * @param {string} projectDir
192
+ */
193
+ const detectSettings = async (projectDir) => {
194
+ const fs = new NodeFS()
195
+ const project = new Project(fs, projectDir)
196
+
197
+ return await project.getBuildSettings()
198
+ }
199
+
200
+ /**
201
+ *
202
+ * @param {import('./types.js').BaseServerSettings | undefined} frameworkSettings
203
+ * @param {import('@netlify/build-info').Settings[]} newSettings
204
+ * @param {Record<string, Record<string, any>>} [metadata]
205
+ */
206
+ const detectChangesInNewSettings = (frameworkSettings, newSettings, metadata) => {
207
+ /** @type {string[]} */
208
+ const message = ['']
209
+ const [setting] = newSettings
210
+
211
+ if (frameworkSettings?.framework !== setting?.framework) {
212
+ message.push(
213
+ `- Framework does not match:`,
214
+ ` [old]: ${frameworkSettings?.framework}`,
215
+ ` [new]: ${setting?.framework}`,
216
+ '',
217
+ )
218
+ }
219
+
220
+ if (frameworkSettings?.command !== setting?.devCommand) {
221
+ message.push(
222
+ `- command does not match:`,
223
+ ` [old]: ${frameworkSettings?.command}`,
224
+ ` [new]: ${setting?.devCommand}`,
225
+ '',
226
+ )
227
+ }
228
+
229
+ if (frameworkSettings?.dist !== setting?.dist) {
230
+ message.push(`- dist does not match:`, ` [old]: ${frameworkSettings?.dist}`, ` [new]: ${setting?.dist}`, '')
231
+ }
232
+
233
+ if (frameworkSettings?.frameworkPort !== setting?.frameworkPort) {
234
+ message.push(
235
+ `- frameworkPort does not match:`,
236
+ ` [old]: ${frameworkSettings?.frameworkPort}`,
237
+ ` [new]: ${setting?.frameworkPort}`,
238
+ '',
239
+ )
240
+ }
241
+
242
+ if (message.length !== 0) {
243
+ reportError(
244
+ {
245
+ name: 'NewSettingsDetectionMismatch',
246
+ errorMessage: 'New Settings detection does not match old one',
247
+ message: message.join('\n'),
248
+ },
249
+ { severity: 'info', metadata },
250
+ )
251
+ }
252
+ }
253
+
185
254
  const detectFrameworkSettings = async ({ projectDir }) => {
186
255
  const projectFrameworks = await listFrameworks({ projectDir })
187
256
  const frameworks = projectFrameworks.filter((framework) => hasDevCommand(framework))
@@ -224,7 +293,7 @@ const hasCommandAndTargetPort = ({ devConfig }) => devConfig.command && devConfi
224
293
  /**
225
294
  * Creates settings for the custom framework
226
295
  * @param {*} param0
227
- * @returns {import('./types').BaseServerSettings}
296
+ * @returns {import('./types.js').BaseServerSettings}
228
297
  */
229
298
  const handleCustomFramework = ({ devConfig }) => {
230
299
  if (!hasCommandAndTargetPort({ devConfig })) {
@@ -271,7 +340,7 @@ const mergeSettings = async ({ devConfig, frameworkSettings = {} }) => {
271
340
  /**
272
341
  * Handles a forced framework and retrieves the settings for it
273
342
  * @param {*} param0
274
- * @returns {Promise<import('./types').BaseServerSettings>}
343
+ * @returns {Promise<import('./types.js').BaseServerSettings>}
275
344
  */
276
345
  const handleForcedFramework = async ({ devConfig, projectDir }) => {
277
346
  // this throws if `devConfig.framework` is not a supported framework
@@ -281,15 +350,16 @@ const handleForcedFramework = async ({ devConfig, projectDir }) => {
281
350
 
282
351
  /**
283
352
  * Get the server settings based on the flags and the devConfig
284
- * @param {import('../commands/dev/types').DevConfig} devConfig
353
+ * @param {import('../commands/dev/types.js').DevConfig} devConfig
285
354
  * @param {import('commander').OptionValues} options
286
355
  * @param {string} projectDir
287
- * @returns {Promise<import('./types').ServerSettings>}
356
+ * @param {Record<string, Record<string, any>>} [metadata]
357
+ * @returns {Promise<import('./types.js').ServerSettings>}
288
358
  */
289
- const detectServerSettings = async (devConfig, options, projectDir) => {
359
+ const detectServerSettings = async (devConfig, options, projectDir, metadata) => {
290
360
  validateStringProperty({ devConfig, property: 'framework' })
291
361
 
292
- /** @type {Partial<import('./types').BaseServerSettings>} */
362
+ /** @type {Partial<import('./types.js').BaseServerSettings>} */
293
363
  let settings = {}
294
364
 
295
365
  if (options.dir || devConfig.framework === '#static') {
@@ -300,6 +370,19 @@ const detectServerSettings = async (devConfig, options, projectDir) => {
300
370
 
301
371
  const runDetection = !hasCommandAndTargetPort({ devConfig })
302
372
  const frameworkSettings = runDetection ? await detectFrameworkSettings({ projectDir }) : undefined
373
+ const newSettings = runDetection ? await detectSettings(projectDir) : undefined
374
+
375
+ // just report differences in the settings
376
+ detectChangesInNewSettings(frameworkSettings, newSettings || [], {
377
+ ...metadata,
378
+ settings: {
379
+ projectDir,
380
+ devConfig,
381
+ options,
382
+ old: frameworkSettings,
383
+ settings: newSettings,
384
+ },
385
+ })
303
386
 
304
387
  if (frameworkSettings === undefined && runDetection) {
305
388
  log(`${NETLIFYDEVWARN} No app server detected. Using simple static server`)
@@ -368,7 +451,7 @@ const formatSettingsArrForInquirer = function (frameworks) {
368
451
  * Returns a copy of the provided config with any plugins provided by the
369
452
  * server settings
370
453
  * @param {*} config
371
- * @param {Partial<import('./types').ServerSettings>} settings
454
+ * @param {Partial<import('./types.js').ServerSettings>} settings
372
455
  * @returns {*} Modified config
373
456
  */
374
457
  export const getConfigWithPlugins = (config, settings) => {
@@ -1,7 +1,10 @@
1
+ import os from 'os'
1
2
  import { dirname, join } from 'path'
2
3
  import process, { version as nodejsVersion } from 'process'
3
4
  import { fileURLToPath } from 'url'
4
5
 
6
+ import { isCI } from 'ci-info'
7
+
5
8
  import execa from '../execa.mjs'
6
9
  import getGlobalConfig from '../get-global-config.mjs'
7
10
 
@@ -14,9 +17,14 @@ const dirPath = dirname(fileURLToPath(import.meta.url))
14
17
  * @param {import('@bugsnag/js').NotifiableError} error
15
18
  * @param {object} config
16
19
  * @param {import('@bugsnag/js').Event['severity']} config.severity
20
+ * @param {Record<string, Record<string, any>>} [config.metadata]
17
21
  * @returns {Promise<void>}
18
22
  */
19
23
  export const reportError = async function (error, config = {}) {
24
+ if (isCI) {
25
+ return
26
+ }
27
+
20
28
  const globalConfig = await getGlobalConfig()
21
29
 
22
30
  const options = JSON.stringify({
@@ -30,15 +38,17 @@ export const reportError = async function (error, config = {}) {
30
38
  user: {
31
39
  id: globalConfig.get('userId'),
32
40
  },
33
- osName: process.platform,
41
+ metadata: config.metadata,
42
+ osName: `${os.platform()}-${os.arch()}`,
34
43
  cliVersion,
35
44
  nodejsVersion,
36
45
  },
37
46
  })
38
47
 
39
- // spawn detached child process to handle send
40
- execa(process.execPath, [join(dirPath, 'request.mjs'), options], {
48
+ // spawn detached child process to handle send and wait for the http request to finish
49
+ // otherwise it can get canceled
50
+ await execa(process.execPath, [join(dirPath, 'request.mjs'), options], {
41
51
  detached: true,
42
52
  stdio: 'ignore',
43
- }).unref()
53
+ })
44
54
  }