netlify-cli 17.17.1 → 17.18.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.
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/commands/dev/dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAU,MAAM,WAAW,CAAA;AA4BhD,OAAO,WAAW,MAAM,oBAAoB,CAAA;AA4D5C,eAAO,MAAM,GAAG,YAAmB,YAAY,WAAW,WAAW,kBAiJpE,CAAA;AAED,eAAO,MAAM,gBAAgB,YAAa,WAAW,gBAuGpD,CAAA"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/commands/dev/dev.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAU,MAAM,WAAW,CAAA;AA4BhD,OAAO,WAAW,MAAM,oBAAoB,CAAA;AA4D5C,eAAO,MAAM,GAAG,YAAmB,YAAY,WAAW,WAAW,kBAwJpE,CAAA;AAED,eAAO,MAAM,gBAAgB,YAAa,WAAW,gBAuGpD,CAAA"}
@@ -102,6 +102,12 @@ export const dev = async (options, command) => {
102
102
  let settings;
103
103
  try {
104
104
  settings = await detectServerSettings(devConfig, options, command);
105
+ if (process.env.NETLIFY_INCLUDE_DEV_SERVER_PLUGIN) {
106
+ if (options.debug) {
107
+ log(`${NETLIFYDEVLOG} Including dev server plugin`);
108
+ }
109
+ settings.plugins = [...(settings.plugins || []), '@netlify/plugin-dev-server'];
110
+ }
105
111
  cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings);
106
112
  }
107
113
  catch (error_) {
@@ -13,17 +13,17 @@ interface QueryParams {
13
13
  fit?: string;
14
14
  position?: string;
15
15
  }
16
- export declare const parseAllDomains: (config: any) => {
16
+ export declare const parseAllRemoteImages: (config: any) => {
17
17
  errors: ErrorObject[];
18
- remoteDomains: string[];
18
+ remotePatterns: RegExp[];
19
19
  };
20
20
  interface ErrorObject {
21
21
  message: string;
22
22
  }
23
- export declare const handleImageDomainsErrors: (errors: ErrorObject[]) => Promise<void>;
24
- export declare const parseRemoteImageDomains: ({ config }: {
23
+ export declare const handleRemoteImagesErrors: (errors: ErrorObject[]) => Promise<void>;
24
+ export declare const parseRemoteImages: ({ config }: {
25
25
  config: any;
26
- }) => Promise<string[]>;
26
+ }) => Promise<RegExp[]>;
27
27
  export declare const isImageRequest: (req: Request) => boolean;
28
28
  export declare const transformImageParams: (query: QueryParams) => string;
29
29
  export declare const initializeProxy: ({ config, settings, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/lib/images/proxy.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAM9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D,eAAO,MAAM,iBAAiB,qBAAqB,CAAA;AAEnD,UAAU,WAAW;IACnB,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAaD,eAAO,MAAM,eAAe;YAAgC,WAAW,EAAE;mBAAiB,MAAM,EAAE;CA2BjG,CAAA;AAED,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAMD,eAAO,MAAM,wBAAwB,WAA2B,WAAW,EAAE,kBAO5E,CAAA;AAGD,eAAO,MAAM,uBAAuB;;uBASnC,CAAA;AAED,eAAO,MAAM,cAAc,QAAkB,OAAO,KAAG,OAEtD,CAAA;AAED,eAAO,MAAM,oBAAoB,UAAoB,WAAW,KAAG,MA4BlE,CAAA;AAED,eAAO,MAAM,eAAe;YAIlB,aAAa;cACX,cAAc;0DAgCzB,CAAA"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/lib/images/proxy.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAM9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAE5D,eAAO,MAAM,iBAAiB,qBAAqB,CAAA;AAEnD,UAAU,WAAW;IACnB,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAaD,eAAO,MAAM,oBAAoB;YAAgC,WAAW,EAAE;oBAAkB,MAAM,EAAE;CAqBvG,CAAA;AAED,UAAU,WAAW;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAMD,eAAO,MAAM,wBAAwB,WAA2B,WAAW,EAAE,kBAO5E,CAAA;AAGD,eAAO,MAAM,iBAAiB;;uBAS7B,CAAA;AAED,eAAO,MAAM,cAAc,QAAkB,OAAO,KAAG,OAEtD,CAAA;AAED,eAAO,MAAM,oBAAoB,UAAoB,WAAW,KAAG,MA4BlE,CAAA;AAED,eAAO,MAAM,eAAe;YAIlB,aAAa;cACX,cAAc;0DA4DzB,CAAA"}
@@ -4,52 +4,43 @@ import { log, NETLIFYDEVERR } from '../../utils/command-helpers.js';
4
4
  import { getProxyUrl } from '../../utils/proxy.js';
5
5
  export const IMAGE_URL_PATTERN = '/.netlify/images';
6
6
  // @ts-expect-error TS(7006) FIXME: Parameter 'config' implicitly has an 'any' type.
7
- export const parseAllDomains = function (config) {
8
- const remoteDomains = [];
7
+ export const parseAllRemoteImages = function (config) {
8
+ const remotePatterns = [];
9
9
  const errors = [];
10
- const domains = config?.images?.remote_images;
11
- if (!domains) {
12
- return { errors, remoteDomains };
10
+ const remoteImages = config?.images?.remote_images;
11
+ if (!remoteImages) {
12
+ return { errors, remotePatterns };
13
13
  }
14
- for (const patternString of domains) {
14
+ for (const patternString of remoteImages) {
15
15
  try {
16
- const url = new URL(patternString);
17
- if (url.hostname) {
18
- remoteDomains.push(url.hostname);
19
- }
20
- else {
21
- errors.push({ message: `The URL '${patternString}' does not have a valid hostname.` });
22
- }
16
+ const urlRegex = new RegExp(patternString);
17
+ remotePatterns.push(urlRegex);
23
18
  }
24
19
  catch (error) {
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
- }
20
+ const message = error instanceof Error ? error.message : 'An unknown error occurred';
21
+ errors.push({ message });
31
22
  }
32
23
  }
33
- return { errors, remoteDomains };
24
+ return { errors, remotePatterns };
34
25
  };
35
26
  const getErrorMessage = function ({ message }) {
36
27
  return message;
37
28
  };
38
- export const handleImageDomainsErrors = async function (errors) {
29
+ export const handleRemoteImagesErrors = async function (errors) {
39
30
  if (errors.length === 0) {
40
31
  return;
41
32
  }
42
33
  const errorMessage = await errors.map(getErrorMessage).join('\n\n');
43
- log(NETLIFYDEVERR, `Image domains syntax errors:\n${errorMessage}`);
34
+ log(NETLIFYDEVERR, `Remote images syntax errors:\n${errorMessage}`);
44
35
  };
45
36
  // @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message
46
- export const parseRemoteImageDomains = async function ({ config }) {
37
+ export const parseRemoteImages = async function ({ config }) {
47
38
  if (!config) {
48
39
  return [];
49
40
  }
50
- const { errors, remoteDomains } = await parseAllDomains(config);
51
- await handleImageDomainsErrors(errors);
52
- return remoteDomains;
41
+ const { errors, remotePatterns } = await parseAllRemoteImages(config);
42
+ await handleRemoteImagesErrors(errors);
43
+ return remotePatterns;
53
44
  };
54
45
  export const isImageRequest = function (req) {
55
46
  return req.url.startsWith(IMAGE_URL_PATTERN);
@@ -79,14 +70,17 @@ export const transformImageParams = function (query) {
79
70
  .join(',');
80
71
  };
81
72
  export const initializeProxy = async function ({ config, settings, }) {
82
- const remoteDomains = await parseRemoteImageDomains({ config });
73
+ const remoteImages = await parseRemoteImages({ config });
83
74
  const devServerUrl = getProxyUrl(settings);
84
75
  const ipx = createIPX({
85
76
  storage: ipxFSStorage({ dir: config?.build?.publish ?? './public' }),
86
- httpStorage: ipxHttpStorage({ domains: [...remoteDomains, devServerUrl] }),
77
+ httpStorage: ipxHttpStorage({
78
+ allowAllDomains: true,
79
+ }),
87
80
  });
88
81
  const handler = createIPXNodeServer(ipx);
89
82
  const app = express();
83
+ let lastTimeRemoteImagesConfigurationDetailsMessageWasLogged = 0;
90
84
  app.use(IMAGE_URL_PATTERN, async (req, res) => {
91
85
  const { url, ...query } = req.query;
92
86
  const sourceImagePath = url;
@@ -95,11 +89,30 @@ export const initializeProxy = async function ({ config, settings, }) {
95
89
  // Construct the full URL for relative paths to request from development server
96
90
  const sourceImagePathWithLeadingSlash = sourceImagePath.startsWith('/') ? sourceImagePath : `/${sourceImagePath}`;
97
91
  const fullImageUrl = `${devServerUrl}${encodeURIComponent(sourceImagePathWithLeadingSlash)}`;
98
- console.log(`fullImageUrl: ${fullImageUrl}`);
99
92
  req.url = `/${modifiers}/${fullImageUrl}`;
100
93
  }
101
94
  else {
102
- // If the image is remote, we can just pass the URL as is
95
+ // If the image is remote, we first check if it's allowed by any of patterns
96
+ if (!remoteImages.some((remoteImage) => remoteImage.test(sourceImagePath))) {
97
+ const remoteImageNotAllowedLogMessage = `Remote image "${sourceImagePath}" source for Image CDN is not allowed.`;
98
+ // Contextual information about the remote image configuration is throttled
99
+ // to avoid spamming the console as it's quite verbose
100
+ // Each not allowed remote image will still be logged, just without configuration details
101
+ if (Date.now() - lastTimeRemoteImagesConfigurationDetailsMessageWasLogged > 1000 * 30) {
102
+ log(`${remoteImageNotAllowedLogMessage}\n\n${remoteImages.length === 0
103
+ ? 'Currently no remote images are allowed.'
104
+ : `Currently allowed remote images configuration details:\n${remoteImages
105
+ .map((pattern) => ` - ${pattern}`)
106
+ .join('\n')}`}\n\nRefer to https://ntl.fyi/remote-images for information about how to configure allowed remote images.`);
107
+ lastTimeRemoteImagesConfigurationDetailsMessageWasLogged = Date.now();
108
+ }
109
+ else {
110
+ log(remoteImageNotAllowedLogMessage);
111
+ }
112
+ res.status(400).end();
113
+ return;
114
+ }
115
+ // Construct the full URL for remote paths
103
116
  req.url = `/${modifiers}/${encodeURIComponent(sourceImagePath)}`;
104
117
  }
105
118
  handler(req, res);