Skip to content

Commit 37bb848

Browse files
committed
Implement URL and URLSearchParams
1 parent 991665c commit 37bb848

9 files changed

+5729
-114
lines changed

Diff for: .eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module.exports = { extends: "@react-native-community" };
1+
module.exports = {extends: '@react-native-community'};

Diff for: .prettierrc

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
2-
"requirePragma": true,
32
"singleQuote": true,
43
"trailingComma": "all",
54
"bracketSpacing": false,

Diff for: babel.config.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
presets: ['module:metro-react-native-babel-preset'],
3+
};

Diff for: js/URL.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import {URL as whatwgUrl} from 'whatwg-url-without-unicode';
2+
import NativeBlobModule from 'react-native/Libraries/Blob/NativeBlobModule';
3+
4+
let BLOB_URL_PREFIX = null;
5+
6+
if (
7+
NativeBlobModule &&
8+
typeof NativeBlobModule.getConstants().BLOB_URI_SCHEME === 'string'
9+
) {
10+
const constants = NativeBlobModule.getConstants();
11+
BLOB_URL_PREFIX = constants.BLOB_URI_SCHEME + ':';
12+
if (typeof constants.BLOB_URI_HOST === 'string') {
13+
BLOB_URL_PREFIX += `//${constants.BLOB_URI_HOST}/`;
14+
}
15+
}
16+
17+
/**
18+
* To allow Blobs be accessed via `content://` URIs,
19+
* you need to register `BlobProvider` as a ContentProvider in your app's `AndroidManifest.xml`:
20+
*
21+
* ```xml
22+
* <manifest>
23+
* <application>
24+
* <provider
25+
* android:name="com.facebook.react.modules.blob.BlobProvider"
26+
* android:authorities="@string/blob_provider_authority"
27+
* android:exported="false"
28+
* />
29+
* </application>
30+
* </manifest>
31+
* ```
32+
* And then define the `blob_provider_authority` string in `res/values/strings.xml`.
33+
* Use a dotted name that's entirely unique to your app:
34+
*
35+
* ```xml
36+
* <resources>
37+
* <string name="blob_provider_authority">your.app.package.blobs</string>
38+
* </resources>
39+
* ```
40+
*/
41+
42+
whatwgUrl.createObjectURL = function createObjectURL(blob) {
43+
if (BLOB_URL_PREFIX === null) {
44+
throw new Error('Cannot create URL for blob!');
45+
}
46+
return `${BLOB_URL_PREFIX}${blob.data.blobId}?offset=${blob.data.offset}&size=${blob.size}`;
47+
};
48+
49+
whatwgUrl.revokeObjectURL = function revokeObjectURL(url) {
50+
// Do nothing.
51+
};
52+
53+
export const URL = whatwgUrl;

Diff for: js/URLSearchParams.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export {URLSearchParams} from 'whatwg-url-without-unicode';

Diff for: js/__tests__/URL-test.js

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {URL} from '../URL';
2+
3+
describe('URL', function() {
4+
it('should pass Mozilla Dev Network examples', () => {
5+
const a = new URL('/', 'https://developer.mozilla.org');
6+
expect(a.href).toBe('https://developer.mozilla.org/');
7+
const b = new URL('https://developer.mozilla.org');
8+
expect(b.href).toBe('https://developer.mozilla.org/');
9+
const c = new URL('en-US/docs', b);
10+
expect(c.href).toBe('https://developer.mozilla.org/en-US/docs');
11+
const d = new URL('/en-US/docs', b);
12+
expect(d.href).toBe('https://developer.mozilla.org/en-US/docs');
13+
const f = new URL('/en-US/docs', d);
14+
expect(f.href).toBe('https://developer.mozilla.org/en-US/docs');
15+
const g = new URL(
16+
'/en-US/docs',
17+
'https://developer.mozilla.org/fr-FR/toto',
18+
);
19+
expect(g.href).toBe('https://developer.mozilla.org/en-US/docs');
20+
const h = new URL('/en-US/docs', a);
21+
expect(h.href).toBe('https://developer.mozilla.org/en-US/docs');
22+
});
23+
24+
it('should pass WHATWG spec examples', () => {
25+
const a = new URL('https:example.org');
26+
expect(a.href).toBe('https://example.org/');
27+
const b = new URL('https://////example.com///');
28+
expect(b.href).toBe('https://example.com///');
29+
const c = new URL('https://example.com/././foo');
30+
expect(c.href).toBe('https://example.com/foo');
31+
const d = new URL('hello:world', 'https://example.com/');
32+
expect(d.href).toBe('hello:world');
33+
const e = new URL('https:example.org', 'https://example.com/');
34+
expect(e.href).toBe('https://example.com/example.org');
35+
const f = new URL('\\example\\..\\demo/.\\', 'https://example.com/');
36+
expect(f.href).toBe('https://example.com/demo/');
37+
const g = new URL('example', 'https://example.com/demo');
38+
expect(g.href).toBe('https://example.com/example');
39+
});
40+
41+
it('should support unicode', () => {
42+
const a = new URL('https://r3---sn-p5qlsnz6.googlevideo.com');
43+
expect(a.href).toBe('https://r3---sn-p5qlsnz6.googlevideo.com/');
44+
});
45+
46+
// https://github.com/facebook/react-native/issues/25717
47+
it('should pass issue #25717 example', () => {
48+
const a = new URL('about', 'https://www.mozilla.org');
49+
expect(a.href).toBe('https://www.mozilla.org/about');
50+
51+
const b = new URL('dev', 'https://google.dev');
52+
expect(b.href).toBe('https://google.dev/dev');
53+
});
54+
55+
// https://github.com/facebook/react-native/issues/24428
56+
it('should pass issue #24428 example', () => {
57+
const url = new URL(
58+
'https://facebook.github.io/react-native/img/header_logo.png',
59+
);
60+
expect(url.href).toBe(
61+
'https://facebook.github.io/react-native/img/header_logo.png',
62+
);
63+
});
64+
});

Diff for: js/__tests__/URLSearchParams-test.js

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import {URLSearchParams} from '../URLSearchParams';
2+
3+
describe('URL', function() {
4+
// https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#Examples
5+
it('should pass Mozilla Dev Network examples', () => {
6+
const paramsString = 'q=URLUtils.searchParams&topic=api';
7+
const searchParams = new URLSearchParams(paramsString);
8+
9+
expect(searchParams.has('topic')).toBe(true);
10+
expect(searchParams.get('topic')).toBe('api');
11+
expect(searchParams.getAll('topic')).toEqual(['api']);
12+
expect(searchParams.get('foo')).toBe(null);
13+
searchParams.append('topic', 'webdev');
14+
expect(searchParams.toString()).toBe(
15+
'q=URLUtils.searchParams&topic=api&topic=webdev',
16+
);
17+
searchParams.set('topic', 'More webdev');
18+
expect(searchParams.toString()).toBe(
19+
'q=URLUtils.searchParams&topic=More+webdev',
20+
);
21+
searchParams.delete('topic');
22+
expect(searchParams.toString()).toBe('q=URLUtils.searchParams');
23+
});
24+
});

Diff for: package.json

+16-2
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,28 @@
1313
},
1414
"main": "index.js",
1515
"scripts": {
16-
"test": "echo \"Error: no test specified\" && exit 1"
16+
"test": "jest"
1717
},
1818
"author": "Nicolas Charpentier <[email protected]>",
1919
"license": "MIT",
20+
"dependencies": {
21+
"buffer": "^5.4.3",
22+
"whatwg-url-without-unicode": "^7.0.0"
23+
},
2024
"devDependencies": {
2125
"@react-native-community/eslint-config": "^0.0.5",
2226
"eslint": "^6.6.0",
2327
"eslint-plugin-prettier": "^3.1.1",
24-
"prettier": "^1.19.1"
28+
"jest": "^24.9.0",
29+
"metro-react-native-babel-preset": "^0.57.0",
30+
"prettier": "^1.19.1",
31+
"react": "^16.12.0",
32+
"react-native": "^0.61.4"
33+
},
34+
"peerDependencies": {
35+
"react-native": "*"
36+
},
37+
"jest": {
38+
"preset": "react-native"
2539
}
2640
}

0 commit comments

Comments
 (0)