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/npm-shrinkwrap.json +240 -1021
- package/package.json +3 -3
- package/src/commands/logs/functions.mjs +28 -5
- package/src/lib/images/proxy.mjs +27 -24
- package/src/utils/proxy.mjs +1 -0
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.
|
|
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.
|
|
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.
|
|
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
|
|
19
|
+
case LOG_LEVELS.INFO:
|
|
9
20
|
logString += chalk.blueBright(logData.level);
|
|
10
21
|
break;
|
|
11
|
-
case
|
|
22
|
+
case LOG_LEVELS.WARN:
|
|
12
23
|
logString += chalk.yellowBright(logData.level);
|
|
13
24
|
break;
|
|
14
|
-
case
|
|
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([
|
|
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);
|
package/src/lib/images/proxy.mjs
CHANGED
|
@@ -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
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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;
|