normalize-url 8.0.2 → 8.1.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/index.d.ts CHANGED
@@ -278,6 +278,45 @@ export type Options = {
278
278
  ```
279
279
  */
280
280
  readonly sortQueryParameters?: boolean;
281
+
282
+ /**
283
+ Removes the entire URL path, leaving only the domain.
284
+
285
+ @default false
286
+
287
+ @example
288
+ ```
289
+ normalizeUrl('https://example.com/path/to/page', {
290
+ removePath: true
291
+ });
292
+ //=> 'https://example.com'
293
+ ```
294
+ */
295
+ readonly removePath?: boolean;
296
+
297
+ /**
298
+ Custom function to transform the URL path components.
299
+
300
+ The function receives an array of non-empty path components and should return a modified array.
301
+
302
+ @default false
303
+
304
+ @example
305
+ ```
306
+ // Keep only the first path component
307
+ normalizeUrl('https://example.com/api/v1/users', {
308
+ transformPath: (pathComponents) => pathComponents.slice(0, 1)
309
+ });
310
+ //=> 'https://example.com/api'
311
+
312
+ // Remove specific components
313
+ normalizeUrl('https://example.com/admin/users', {
314
+ transformPath: (pathComponents) => pathComponents.filter(c => c !== 'admin')
315
+ });
316
+ //=> 'https://example.com/users'
317
+ ```
318
+ */
319
+ readonly transformPath?: (pathComponents: string[]) => string[];
281
320
  };
282
321
 
283
322
  /**
package/index.js CHANGED
@@ -89,6 +89,8 @@ export default function normalizeUrl(urlString, options) {
89
89
  removeDirectoryIndex: false,
90
90
  removeExplicitPort: false,
91
91
  sortQueryParameters: true,
92
+ removePath: false,
93
+ transformPath: false,
92
94
  ...options,
93
95
  };
94
96
 
@@ -200,6 +202,18 @@ export default function normalizeUrl(urlString, options) {
200
202
  }
201
203
  }
202
204
 
205
+ // Remove path
206
+ if (options.removePath) {
207
+ urlObject.pathname = '/';
208
+ }
209
+
210
+ // Transform path components
211
+ if (options.transformPath && typeof options.transformPath === 'function') {
212
+ const pathComponents = urlObject.pathname.split('/').filter(Boolean);
213
+ const newComponents = options.transformPath(pathComponents);
214
+ urlObject.pathname = newComponents?.length > 0 ? `/${newComponents.join('/')}` : '/';
215
+ }
216
+
203
217
  if (urlObject.hostname) {
204
218
  // Remove trailing dot
205
219
  urlObject.hostname = urlObject.hostname.replace(/\.$/, '');
@@ -240,12 +254,21 @@ export default function normalizeUrl(urlString, options) {
240
254
 
241
255
  // Sort query parameters
242
256
  if (options.sortQueryParameters) {
257
+ const originalSearch = urlObject.search;
243
258
  urlObject.searchParams.sort();
244
259
 
245
260
  // Calling `.sort()` encodes the search parameters, so we need to decode them again.
246
261
  try {
247
262
  urlObject.search = decodeURIComponent(urlObject.search);
248
263
  } catch {}
264
+
265
+ // Fix parameters that originally had no equals sign but got one added by URLSearchParams
266
+ const partsWithoutEquals = originalSearch.slice(1).split('&').filter(p => p && !p.includes('='));
267
+ for (const part of partsWithoutEquals) {
268
+ const decoded = decodeURIComponent(part);
269
+ // Only replace at word boundaries to avoid partial matches
270
+ urlObject.search = urlObject.search.replace(`?${decoded}=`, `?${decoded}`).replace(`&${decoded}=`, `&${decoded}`);
271
+ }
249
272
  }
250
273
 
251
274
  if (options.removeTrailingSlash) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "normalize-url",
3
- "version": "8.0.2",
3
+ "version": "8.1.0",
4
4
  "description": "Normalize a URL",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/normalize-url",
package/readme.md CHANGED
@@ -307,6 +307,41 @@ normalizeUrl('www.sindresorhus.com?b=two&a=one&c=three', {
307
307
  //=> 'http://sindresorhus.com/?b=two&a=one&c=three'
308
308
  ```
309
309
 
310
+ ##### removePath
311
+
312
+ Type: `boolean`\
313
+ Default: `false`
314
+
315
+ Removes the entire URL path, leaving only the domain.
316
+
317
+ ```js
318
+ normalizeUrl('https://example.com/path/to/page', {
319
+ removePath: true
320
+ });
321
+ //=> 'https://example.com'
322
+ ```
323
+
324
+ ##### transformPath
325
+
326
+ Type: `Function`\
327
+ Default: `false`
328
+
329
+ Custom function to transform the URL path components. The function receives an array of non-empty path components and should return a modified array.
330
+
331
+ ```js
332
+ // Keep only the first path component
333
+ normalizeUrl('https://example.com/api/v1/users', {
334
+ transformPath: (pathComponents) => pathComponents.slice(0, 1)
335
+ });
336
+ //=> 'https://example.com/api'
337
+
338
+ // Remove specific components
339
+ normalizeUrl('https://example.com/admin/users', {
340
+ transformPath: (pathComponents) => pathComponents.filter(c => c !== 'admin')
341
+ });
342
+ //=> 'https://example.com/users'
343
+ ```
344
+
310
345
  ## Related
311
346
 
312
347
  - [compare-urls](https://github.com/sindresorhus/compare-urls) - Compare URLs by first normalizing them