forked from angular/angular-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathurl.ts
More file actions
237 lines (219 loc) · 7.88 KB
/
url.ts
File metadata and controls
237 lines (219 loc) · 7.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
/**
* Removes the trailing slash from a URL if it exists.
*
* @param url - The URL string from which to remove the trailing slash.
* @returns The URL string without a trailing slash.
*
* @example
* ```js
* stripTrailingSlash('path/'); // 'path'
* stripTrailingSlash('/path'); // '/path'
* stripTrailingSlash('/'); // '/'
* stripTrailingSlash(''); // ''
* ```
*/
export function stripTrailingSlash(url: string): string {
// Check if the last character of the URL is a slash
return url.length > 1 && url[url.length - 1] === '/' ? url.slice(0, -1) : url;
}
/**
* Removes the leading slash from a URL if it exists.
*
* @param url - The URL string from which to remove the leading slash.
* @returns The URL string without a leading slash.
*
* @example
* ```js
* stripLeadingSlash('/path'); // 'path'
* stripLeadingSlash('/path/'); // 'path/'
* stripLeadingSlash('/'); // '/'
* stripLeadingSlash(''); // ''
* ```
*/
export function stripLeadingSlash(url: string): string {
// Check if the first character of the URL is a slash
return url.length > 1 && url[0] === '/' ? url.slice(1) : url;
}
/**
* Adds a leading slash to a URL if it does not already have one.
*
* @param url - The URL string to which the leading slash will be added.
* @returns The URL string with a leading slash.
*
* @example
* ```js
* addLeadingSlash('path'); // '/path'
* addLeadingSlash('/path'); // '/path'
* ```
*/
export function addLeadingSlash(url: string): string {
// Check if the URL already starts with a slash
return url[0] === '/' ? url : `/${url}`;
}
/**
* Adds a trailing slash to a URL if it does not already have one.
*
* @param url - The URL string to which the trailing slash will be added.
* @returns The URL string with a trailing slash.
*
* @example
* ```js
* addTrailingSlash('path'); // 'path/'
* addTrailingSlash('path/'); // 'path/'
* ```
*/
export function addTrailingSlash(url: string): string {
// Check if the URL already end with a slash
return url[url.length - 1] === '/' ? url : `${url}/`;
}
/**
* Joins URL parts into a single URL string.
*
* This function takes multiple URL segments, normalizes them by removing leading
* and trailing slashes where appropriate, and then joins them into a single URL.
*
* @param parts - The parts of the URL to join. Each part can be a string with or without slashes.
* @returns The joined URL string, with normalized slashes.
*
* @example
* ```js
* joinUrlParts('path/', '/to/resource'); // '/path/to/resource'
* joinUrlParts('/path/', 'to/resource'); // '/path/to/resource'
* joinUrlParts('', ''); // '/'
* ```
*/
export function joinUrlParts(...parts: string[]): string {
const normalizeParts: string[] = [];
for (const part of parts) {
if (part === '') {
// Skip any empty parts
continue;
}
let normalizedPart = part;
if (part[0] === '/') {
normalizedPart = normalizedPart.slice(1);
}
if (part[part.length - 1] === '/') {
normalizedPart = normalizedPart.slice(0, -1);
}
if (normalizedPart !== '') {
normalizeParts.push(normalizedPart);
}
}
return addLeadingSlash(normalizeParts.join('/'));
}
/**
* Strips `/index.html` from the end of a URL's path, if present.
*
* This function is used to convert URLs pointing to an `index.html` file into their directory
* equivalents. For example, it transforms a URL like `http://www.example.com/page/index.html`
* into `http://www.example.com/page`.
*
* @param url - The URL object to process.
* @returns A new URL object with `/index.html` removed from the path, if it was present.
*
* @example
* ```typescript
* const originalUrl = new URL('http://www.example.com/page/index.html');
* const cleanedUrl = stripIndexHtmlFromURL(originalUrl);
* console.log(cleanedUrl.href); // Output: 'http://www.example.com/page'
* ```
*/
export function stripIndexHtmlFromURL(url: URL): URL {
if (url.pathname.endsWith('/index.html')) {
const modifiedURL = new URL(url);
// Remove '/index.html' from the pathname
modifiedURL.pathname = modifiedURL.pathname.slice(0, /** '/index.html'.length */ -11);
return modifiedURL;
}
return url;
}
/**
* Resolves `*` placeholders in a path template by mapping them to corresponding segments
* from a base path. This is useful for constructing paths dynamically based on a given base path.
*
* The function processes the `toPath` string, replacing each `*` placeholder with
* the corresponding segment from the `fromPath`. If the `toPath` contains no placeholders,
* it is returned as-is. Invalid `toPath` formats (not starting with `/`) will throw an error.
*
* @param toPath - A path template string that may contain `*` placeholders. Each `*` is replaced
* by the corresponding segment from the `fromPath`. Static paths (e.g., `/static/path`) are returned
* directly without placeholder replacement.
* @param fromPath - A base path string, split into segments, that provides values for
* replacing `*` placeholders in the `toPath`.
* @returns A resolved path string with `*` placeholders replaced by segments from the `fromPath`,
* or the `toPath` returned unchanged if it contains no placeholders.
*
* @throws If the `toPath` does not start with a `/`, indicating an invalid path format.
*
* @example
* ```typescript
* // Example with placeholders resolved
* const resolvedPath = buildPathWithParams('/*\/details', '/123/abc');
* console.log(resolvedPath); // Outputs: '/123/details'
*
* // Example with a static path
* const staticPath = buildPathWithParams('/static/path', '/base/unused');
* console.log(staticPath); // Outputs: '/static/path'
* ```
*/
export function buildPathWithParams(toPath: string, fromPath: string): string {
if (toPath[0] !== '/') {
throw new Error(`Invalid toPath: The string must start with a '/'. Received: '${toPath}'`);
}
if (fromPath[0] !== '/') {
throw new Error(`Invalid fromPath: The string must start with a '/'. Received: '${fromPath}'`);
}
if (!toPath.includes('/*')) {
return toPath;
}
const fromPathParts = fromPath.split('/');
const toPathParts = toPath.split('/');
const resolvedParts = toPathParts.map((part, index) =>
toPathParts[index] === '*' ? fromPathParts[index] : part,
);
return joinUrlParts(...resolvedParts);
}
const MATRIX_PARAMS_REGEX = /;[^/]+/g;
/**
* Removes Angular matrix parameters from a given URL path.
*
* This function takes a URL path string and removes any matrix parameters.
* Matrix parameters are parts of a URL segment that start with a semicolon `;`.
*
* @param pathname - The URL path to remove matrix parameters from.
* @returns The URL path with matrix parameters removed.
*
* @example
* ```ts
* stripMatrixParams('/path;param=value'); // returns '/path'
* stripMatrixParams('/path;param=value/to;p=1/resource'); // returns '/path/to/resource'
* stripMatrixParams('/path/to/resource'); // returns '/path/to/resource'
* ```
*/
export function stripMatrixParams(pathname: string): string {
// Use a regular expression to remove matrix parameters.
// This regex finds all occurrences of a semicolon followed by any characters
return pathname.includes(';') ? pathname.replace(MATRIX_PARAMS_REGEX, '') : pathname;
}
/**
* Constructs a decoded URL string from its components.
*
* This function joins the pathname (with trailing slash removed), search, and hash,
* and then decodes the result.
*
* @param pathname - The path of the URL.
* @param search - The query string of the URL (including '?').
* @param hash - The hash fragment of the URL (including '#').
* @returns The constructed and decoded URL string.
*/
export function constructUrl(pathname: string, search: string, hash: string): string {
return decodeURIComponent([stripTrailingSlash(pathname), search, hash].join(''));
}