Skip to content

Commit 59eb5df

Browse files
fix: url-issues
1 parent b846305 commit 59eb5df

File tree

2 files changed

+62
-3
lines changed

2 files changed

+62
-3
lines changed

packages/react-native/Libraries/Blob/URL.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,15 @@ export class URL {
9090
}
9191
}
9292

93+
// Only add trailing slash if URL has no path (just domain)
9394
if (
9495
!this._url.endsWith('/') &&
9596
!(this._url.includes('?') || this._url.includes('#'))
9697
) {
97-
this._url += '/';
98+
const afterProtocol = this._url.split('://')[1];
99+
if (afterProtocol && !afterProtocol.includes('/')) {
100+
this._url += '/';
101+
}
98102
}
99103
} else {
100104
if (typeof base === 'string') {
@@ -170,6 +174,24 @@ export class URL {
170174
return searchMatch ? `?${searchMatch[1]}` : '';
171175
}
172176

177+
set search(value: string) {
178+
// Remove leading '?' if present
179+
const searchString = value.startsWith('?') ? value.slice(1) : value;
180+
181+
// Update the internal URL
182+
const baseUrl = this._url.split('?')[0].split('#')[0];
183+
const hash = this.hash;
184+
185+
if (searchString) {
186+
this._url = baseUrl + '?' + searchString + hash;
187+
} else {
188+
this._url = baseUrl + hash;
189+
}
190+
191+
// Reset the searchParams instance so it gets recreated with new values
192+
this._searchParamsInstance = null;
193+
}
194+
173195
get searchParams(): URLSearchParams {
174196
if (this._searchParamsInstance == null) {
175197
this._searchParamsInstance = new URLSearchParams(this.search);
@@ -185,10 +207,19 @@ export class URL {
185207
if (this._searchParamsInstance === null) {
186208
return this._url;
187209
}
210+
211+
// Remove existing search params and hash from the URL
212+
const baseUrl = this._url.split('?')[0].split('#')[0];
213+
const hash = this.hash;
214+
188215
// $FlowFixMe[incompatible-use]
189216
const instanceString = this._searchParamsInstance.toString();
190-
const separator = this._url.indexOf('?') > -1 ? '&' : '?';
191-
return this._url + separator + instanceString;
217+
218+
if (instanceString) {
219+
return baseUrl + '?' + instanceString + hash;
220+
} else {
221+
return baseUrl + hash;
222+
}
192223
}
193224

194225
get username(): string {

packages/react-native/Libraries/Blob/__tests__/URL-test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,33 @@ describe('URL', function () {
139139
);
140140
unsortedParams.sort();
141141
expect(unsortedParams.toString()).toBe('a=first&b=second&c=third&z=last');
142+
143+
// searchParams.set() should replace values not duplicate them
144+
const urlWithSearchParams = new URL(
145+
'https://example.com/api/data?foo=bar&baz=qux',
146+
);
147+
urlWithSearchParams.searchParams.set('foo', 'newFoo');
148+
urlWithSearchParams.searchParams.set('test', '12345');
149+
expect(urlWithSearchParams.toString()).toBe(
150+
'https://example.com/api/data?foo=newFoo&baz=qux&test=12345',
151+
);
152+
153+
//url.search setter should replace search params
154+
const urlSearchSetter = new URL(
155+
'https://example.com/api/data?foo=bar&baz=qux',
156+
);
157+
158+
urlSearchSetter.search = 'foo=overwritten&hello=world';
159+
160+
expect(urlSearchSetter.toString()).toBe(
161+
'https://example.com/api/data?foo=overwritten&hello=world',
162+
);
163+
164+
//URL constructor should not add trailing slash to paths
165+
const urlWithoutTrailing = new URL('https://example.com/path/to/resource');
166+
167+
expect(urlWithoutTrailing.toString()).toBe(
168+
'https://example.com/path/to/resource',
169+
);
142170
});
143171
});

0 commit comments

Comments
 (0)