mirror of
https://github.com/status-im/react-native.git
synced 2025-02-23 06:38:13 +00:00
Flesh out the URL polyfill a bit more (#22901)
Summary:
This expands functionality of URL minimally so Apollo Server can run in React Native contexts. Add explicit-fail getters so undefined values won't get generated from the otherwise missing implemenation.
Use of URL in apollo-server here: 458bc71ead/packages/apollo-datasource-rest/src/RESTDataSource.ts (L79)
Credit to my colleague dysonpro for debugging the issue and providing the initial working stub implementation.
Changelog:
----------
Help reviewers and the release process by writing your own changelog entry. See http://facebook.github.io/react-native/docs/contributing#changelog for an example.
[INTERNAL] [ENHANCEMENT] - Support construction, toString(), and href() of URL objects.
Pull Request resolved: https://github.com/facebook/react-native/pull/22901
Differential Revision: D13690954
Pulled By: cpojer
fbshipit-source-id: 7966bc17be8af9bf656bffea5d530b1e626acfb3
This commit is contained in:
parent
e3ff15052a
commit
63038500a2
@ -5,7 +5,6 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*
|
*
|
||||||
* @format
|
* @format
|
||||||
* @flow strict-local
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
@ -47,11 +46,71 @@ if (BlobModule && typeof BlobModule.BLOB_URI_SCHEME === 'string') {
|
|||||||
* </resources>
|
* </resources>
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
class URL {
|
|
||||||
constructor() {
|
// Small subset from whatwg-url: https://github.com/jsdom/whatwg-url/tree/master/lib
|
||||||
throw new Error('Creating URL objects is not supported yet.');
|
// The reference code bloat comes from Unicode issues with URLs, so those won't work here.
|
||||||
|
export class URLSearchParams {
|
||||||
|
_searchParams = [];
|
||||||
|
|
||||||
|
constructor(params: any) {
|
||||||
|
if (typeof params === 'object') {
|
||||||
|
Object.keys(params).forEach(key => this.append(key, params[key]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
append(key: string, value: string) {
|
||||||
|
this._searchParams.push([key, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(name) {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get(name) {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
getAll(name) {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
has(name) {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
set(name, value) {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
sort() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return this._searchParams[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
if (this._searchParams.length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
const last = this._searchParams.length - 1;
|
||||||
|
return this._searchParams.reduce((acc, curr, index) => {
|
||||||
|
return acc + curr.join('=') + (index === last ? '' : '&');
|
||||||
|
}, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateBaseUrl(url: string) {
|
||||||
|
// from this MIT-licensed gist: https://gist.github.com/dperini/729294
|
||||||
|
return /^(?:(?:(?:https?|ftp):)?\/\/)(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(
|
||||||
|
url,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export class URL {
|
||||||
|
_searchParamsInstance = null;
|
||||||
|
|
||||||
static createObjectURL(blob: Blob) {
|
static createObjectURL(blob: Blob) {
|
||||||
if (BLOB_URL_PREFIX === null) {
|
if (BLOB_URL_PREFIX === null) {
|
||||||
throw new Error('Cannot create URL for blob!');
|
throw new Error('Cannot create URL for blob!');
|
||||||
@ -64,6 +123,93 @@ class URL {
|
|||||||
static revokeObjectURL(url: string) {
|
static revokeObjectURL(url: string) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = URL;
|
constructor(url: string, base: string) {
|
||||||
|
let baseUrl = null;
|
||||||
|
if (base) {
|
||||||
|
if (typeof base === 'string') {
|
||||||
|
baseUrl = base;
|
||||||
|
if (!validateBaseUrl(baseUrl)) {
|
||||||
|
throw new TypeError(`Invalid base URL: ${baseUrl}`);
|
||||||
|
}
|
||||||
|
} else if (typeof base === 'object') {
|
||||||
|
baseUrl = base.toString();
|
||||||
|
}
|
||||||
|
if (baseUrl.endsWith('/') && url.startsWith('/')) {
|
||||||
|
baseUrl = baseUrl.slice(0, baseUrl.length - 1);
|
||||||
|
}
|
||||||
|
if (baseUrl.endsWith(url)) {
|
||||||
|
url = '';
|
||||||
|
}
|
||||||
|
this._url = `${baseUrl}${url}`;
|
||||||
|
} else {
|
||||||
|
this._url = url;
|
||||||
|
if (!this._url.endsWith('/')) {
|
||||||
|
this._url += '/';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get hash() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get host() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get hostname() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get href(): string {
|
||||||
|
return this.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
get origin() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get password() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get pathname() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get port() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get protocol() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get search() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get searchParams(): URLSearchParams {
|
||||||
|
if (this._searchParamsInstance == null) {
|
||||||
|
this._searchParamsInstance = new URLSearchParams();
|
||||||
|
}
|
||||||
|
return this._searchParamsInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): string {
|
||||||
|
return this.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
if (this._searchParamsInstance === null) {
|
||||||
|
return this._url;
|
||||||
|
}
|
||||||
|
const separator = this._url.indexOf('?') > -1 ? '&' : '?';
|
||||||
|
return this._url + separator + this._searchParamsInstance.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
get username() {
|
||||||
|
throw new Error('not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
35
Libraries/Blob/__tests__/URL-test.js
Normal file
35
Libraries/Blob/__tests__/URL-test.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
* @emails oncall+react_native
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const URL = require('URL').URL;
|
||||||
|
|
||||||
|
describe('URL', function() {
|
||||||
|
it('should pass Mozilla Dev Network examples', () => {
|
||||||
|
const a = new URL('/', 'https://developer.mozilla.org');
|
||||||
|
expect(a.href).toBe('https://developer.mozilla.org/');
|
||||||
|
const b = new URL('https://developer.mozilla.org');
|
||||||
|
expect(b.href).toBe('https://developer.mozilla.org/');
|
||||||
|
const c = new URL('en-US/docs', b);
|
||||||
|
expect(c.href).toBe('https://developer.mozilla.org/en-US/docs');
|
||||||
|
const d = new URL('/en-US/docs', b);
|
||||||
|
expect(d.href).toBe('https://developer.mozilla.org/en-US/docs');
|
||||||
|
const f = new URL('/en-US/docs', d);
|
||||||
|
expect(f.href).toBe('https://developer.mozilla.org/en-US/docs');
|
||||||
|
// from original test suite, but requires complex implementation
|
||||||
|
// const g = new URL(
|
||||||
|
// '/en-US/docs',
|
||||||
|
// 'https://developer.mozilla.org/fr-FR/toto',
|
||||||
|
// );
|
||||||
|
// expect(g.href).toBe('https://developer.mozilla.org/en-US/docs');
|
||||||
|
const h = new URL('/en-US/docs', a);
|
||||||
|
expect(h.href).toBe('https://developer.mozilla.org/en-US/docs');
|
||||||
|
});
|
||||||
|
});
|
@ -28,4 +28,5 @@ polyfillGlobal('WebSocket', () => require('WebSocket'));
|
|||||||
polyfillGlobal('Blob', () => require('Blob'));
|
polyfillGlobal('Blob', () => require('Blob'));
|
||||||
polyfillGlobal('File', () => require('File'));
|
polyfillGlobal('File', () => require('File'));
|
||||||
polyfillGlobal('FileReader', () => require('FileReader'));
|
polyfillGlobal('FileReader', () => require('FileReader'));
|
||||||
polyfillGlobal('URL', () => require('URL'));
|
polyfillGlobal('URL', () => require('URL').URL); // flowlint-line untyped-import:off
|
||||||
|
polyfillGlobal('URLSearchParams', () => require('URL').URLSearchParams); // flowlint-line untyped-import:off
|
||||||
|
Loading…
x
Reference in New Issue
Block a user