netlify-cli 17.5.2 → 17.6.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
3
  "description": "Netlify command line tool",
4
- "version": "17.5.2",
4
+ "version": "17.6.0",
5
5
  "author": "Netlify Inc.",
6
6
  "type": "module",
7
7
  "engines": {
@@ -47,12 +47,12 @@
47
47
  "@bugsnag/js": "7.20.2",
48
48
  "@fastify/static": "6.10.2",
49
49
  "@netlify/blobs": "6.3.0",
50
- "@netlify/build": "29.26.6",
50
+ "@netlify/build": "29.27.0",
51
51
  "@netlify/build-info": "7.11.1",
52
52
  "@netlify/config": "20.10.0",
53
53
  "@netlify/edge-bundler": "10.1.3",
54
54
  "@netlify/local-functions-proxy": "1.1.1",
55
- "@netlify/zip-it-and-ship-it": "9.26.1",
55
+ "@netlify/zip-it-and-ship-it": "9.26.2",
56
56
  "@octokit/rest": "19.0.13",
57
57
  "ansi-escapes": "6.2.0",
58
58
  "ansi-styles": "6.2.1",
@@ -1,17 +1,28 @@
1
- import { Argument } from 'commander';
1
+ import { Argument, Option } from 'commander';
2
2
  import inquirer from 'inquirer';
3
3
  import { chalk, log } from '../../utils/command-helpers.mjs';
4
4
  import { getWebSocket } from '../../utils/websockets/index.mjs';
5
+ // Source: Source: https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced
6
+ export const LOG_LEVELS = {
7
+ TRACE: 'TRACE',
8
+ DEBUG: 'DEBUG',
9
+ INFO: 'INFO',
10
+ WARN: 'WARN',
11
+ ERROR: 'ERROR',
12
+ FATAL: 'FATAL',
13
+ };
14
+ const LOG_LEVELS_LIST = Object.values(LOG_LEVELS).map((level) => level.toLowerCase());
15
+ const CLI_LOG_LEVEL_CHOICES_STRING = LOG_LEVELS_LIST.map((level) => ` ${level}`);
5
16
  function getLog(logData) {
6
17
  let logString = '';
7
18
  switch (logData.level) {
8
- case 'INFO':
19
+ case LOG_LEVELS.INFO:
9
20
  logString += chalk.blueBright(logData.level);
10
21
  break;
11
- case 'WARN':
22
+ case LOG_LEVELS.WARN:
12
23
  logString += chalk.yellowBright(logData.level);
13
24
  break;
14
- case 'ERROR':
25
+ case LOG_LEVELS.ERROR:
15
26
  logString += chalk.redBright(logData.level);
16
27
  break;
17
28
  default:
@@ -24,6 +35,10 @@ const logsFunction = async (functionName, options, command) => {
24
35
  const client = command.netlify.api;
25
36
  const { site } = command.netlify;
26
37
  const { id: siteId } = site;
38
+ if (options.level && !options.level.every((level) => LOG_LEVELS_LIST.includes(level))) {
39
+ log(`Invalid log level. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`);
40
+ }
41
+ const levelsToPrint = options.level || LOG_LEVELS_LIST;
27
42
  const { functions = [] } = await client.searchSiteFunctions({ siteId });
28
43
  if (functions.length === 0) {
29
44
  log(`No functions found for the site`);
@@ -58,6 +73,9 @@ const logsFunction = async (functionName, options, command) => {
58
73
  });
59
74
  ws.on('message', (data) => {
60
75
  const logData = JSON.parse(data);
76
+ if (!levelsToPrint.includes(logData.level.toLowerCase())) {
77
+ return;
78
+ }
61
79
  log(getLog(logData));
62
80
  });
63
81
  ws.on('close', () => {
@@ -71,7 +89,12 @@ const logsFunction = async (functionName, options, command) => {
71
89
  export const createLogsFunctionCommand = (program) => program
72
90
  .command('logs:function')
73
91
  .alias('logs:functions')
92
+ .addOption(new Option('-l, --level <levels...>', `Log levels to stream. Choices are:${CLI_LOG_LEVEL_CHOICES_STRING}`))
74
93
  .addArgument(new Argument('[functionName]', 'Name of the function to stream logs for'))
75
- .addExamples(['netlify logs:function my-function', 'netlify logs:function'])
94
+ .addExamples([
95
+ 'netlify logs:function',
96
+ 'netlify logs:function my-function',
97
+ 'netlify logs:function my-function -l info warn',
98
+ ])
76
99
  .description('(Beta) Stream netlify function logs to the console')
77
100
  .action(logsFunction);
@@ -1,15 +1,16 @@
1
1
  import express from 'express';
2
2
  import { createIPX, ipxFSStorage, ipxHttpStorage, createIPXNodeServer } from 'ipx';
3
3
  import { log, NETLIFYDEVERR } from '../../utils/command-helpers.mjs';
4
+ import { getProxyUrl } from '../../utils/proxy.mjs';
4
5
  export const IMAGE_URL_PATTERN = '/.netlify/images';
5
6
  // @ts-expect-error TS(7006) FIXME: Parameter 'config' implicitly has an 'any' type.
6
7
  export const parseAllDomains = function (config) {
8
+ const remoteDomains = [];
9
+ const errors = [];
7
10
  const domains = config?.images?.remote_images;
8
11
  if (!domains) {
9
- return { errors: [], remoteDomains: [] };
12
+ return { errors, remoteDomains };
10
13
  }
11
- const remoteDomains = [];
12
- const errors = [];
13
14
  for (const patternString of domains) {
14
15
  try {
15
16
  const url = new URL(patternString);
@@ -17,21 +18,23 @@ export const parseAllDomains = function (config) {
17
18
  remoteDomains.push(url.hostname);
18
19
  }
19
20
  else {
20
- errors.push(`The URL '${patternString}' does not have a valid hostname.`);
21
+ errors.push({ message: `The URL '${patternString}' does not have a valid hostname.` });
21
22
  }
22
23
  }
23
24
  catch (error) {
24
- // @ts-expect-error TS(2571) FIXME: Object is of type 'unknown'.
25
- errors.push(`Invalid URL '${patternString}': ${error.message}`);
25
+ if (error instanceof Error) {
26
+ errors.push({ message: `Invalid URL '${patternString}': ${error.message}` });
27
+ }
28
+ else {
29
+ errors.push({ message: `Invalid URL '${patternString}': An unknown error occurred` });
30
+ }
26
31
  }
27
32
  }
28
33
  return { errors, remoteDomains };
29
34
  };
30
- // @ts-expect-error TS(7031) FIXME: Binding element 'message' implicitly has an 'any' ... Remove this comment to see the full error message
31
35
  const getErrorMessage = function ({ message }) {
32
36
  return message;
33
37
  };
34
- // @ts-expect-error TS(7006) FIXME: Parameter 'errors' implicitly has an 'any' type.
35
38
  export const handleImageDomainsErrors = async function (errors) {
36
39
  if (errors.length === 0) {
37
40
  return;
@@ -48,57 +51,57 @@ export const parseRemoteImageDomains = async function ({ config }) {
48
51
  await handleImageDomainsErrors(errors);
49
52
  return remoteDomains;
50
53
  };
51
- // @ts-expect-error TS(7006) FIXME: Parameter 'req' implicitly has an 'any' type.
52
54
  export const isImageRequest = function (req) {
53
55
  return req.url.startsWith(IMAGE_URL_PATTERN);
54
56
  };
55
- // @ts-expect-error TS(7006) FIXME: Parameter 'query' implicitly has an 'any' type.
56
57
  export const transformImageParams = function (query) {
57
58
  const params = {};
58
59
  const width = query.w || query.width || null;
59
60
  const height = query.h || query.height || null;
60
61
  if (width && height) {
61
- // @ts-expect-error TS(2339) FIXME: Property 's' does not exist on type '{}'.
62
62
  // eslint-disable-next-line id-length
63
63
  params.s = `${width}x${height}`;
64
64
  }
65
65
  else {
66
- // @ts-expect-error TS(2339) FIXME: Property 'w' does not exist on type '{}'.
67
66
  // eslint-disable-next-line id-length
68
67
  params.w = width;
69
- // @ts-expect-error TS(2339) FIXME: Property 'j' does not exist on type '{}'.
70
68
  // eslint-disable-next-line id-length
71
69
  params.h = height;
72
70
  }
73
- // @ts-expect-error TS(2339) FIXME: Property 'quality' does not exist on type '{}'.
74
71
  params.quality = query.q || query.quality || null;
75
- // @ts-expect-error TS(2339) FIXME: Property 'format' does not exist on type '{}'.
76
72
  params.format = query.fm || null;
77
73
  const fit = query.fit || null;
78
- // @ts-expect-error TS(2339) FIXME: Property 'fit' does not exist on type '{}'.
79
74
  params.fit = fit === 'contain' ? 'inside' : fit;
80
- // @ts-expect-error TS(2339) FIXME: Property 'position' does not exist on type '{}'.
81
75
  params.position = query.position || null;
82
76
  return Object.entries(params)
83
77
  .filter(([, value]) => value !== null)
84
78
  .map(([key, value]) => `${key}_${value}`)
85
79
  .join(',');
86
80
  };
87
- // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message
88
- export const initializeProxy = async function ({ config }) {
81
+ export const initializeProxy = async function ({ config, settings, }) {
89
82
  const remoteDomains = await parseRemoteImageDomains({ config });
83
+ const devServerUrl = getProxyUrl(settings);
90
84
  const ipx = createIPX({
91
85
  storage: ipxFSStorage({ dir: config?.build?.publish ?? './public' }),
92
- httpStorage: ipxHttpStorage({ domains: remoteDomains }),
86
+ httpStorage: ipxHttpStorage({ domains: [...remoteDomains, devServerUrl] }),
93
87
  });
94
88
  const handler = createIPXNodeServer(ipx);
95
89
  const app = express();
96
90
  app.use(IMAGE_URL_PATTERN, async (req, res) => {
97
91
  const { url, ...query } = req.query;
98
- const modifiers = await transformImageParams(query);
99
- // @ts-expect-error TS(2345) FIXME: Argument of type 'string | string[] | ParsedQs | P... Remove this comment to see the full error message
100
- const path = `/${modifiers}/${encodeURIComponent(url)}`;
101
- req.url = path;
92
+ const sourceImagePath = url;
93
+ const modifiers = (await transformImageParams(query)) || `_`;
94
+ if (!sourceImagePath.startsWith('http://') && !sourceImagePath.startsWith('https://')) {
95
+ // Construct the full URL for relative paths to request from development server
96
+ const sourceImagePathWithLeadingSlash = sourceImagePath.startsWith('/') ? sourceImagePath : `/${sourceImagePath}`;
97
+ const fullImageUrl = `${devServerUrl}/${encodeURIComponent(sourceImagePathWithLeadingSlash)}`;
98
+ console.log(`fullImageUrl: ${fullImageUrl}`);
99
+ req.url = `/${modifiers}/${fullImageUrl}`;
100
+ }
101
+ else {
102
+ // If the image is remote, we can just pass the URL as is
103
+ req.url = `/${modifiers}/${encodeURIComponent(sourceImagePath)}`;
104
+ }
102
105
  handler(req, res);
103
106
  });
104
107
  return app;
@@ -701,6 +701,7 @@ state, }) {
701
701
  });
702
702
  const imageProxy = await initializeImageProxy({
703
703
  config,
704
+ settings,
704
705
  });
705
706
  const proxy = await initializeProxy({
706
707
  env,