diff --git a/packages/react-native/Libraries/Blob/URL.js b/packages/react-native/Libraries/Blob/URL.js index 8763926eb22a2e..db4bb2792a1e3a 100644 --- a/packages/react-native/Libraries/Blob/URL.js +++ b/packages/react-native/Libraries/Blob/URL.js @@ -27,7 +27,7 @@ if ( } } -/** +/* * To allow Blobs be accessed via `content://` URIs, * you need to register `BlobProvider` as a ContentProvider in your app's `AndroidManifest.xml`: * @@ -52,67 +52,7 @@ if ( * ``` */ -// Small subset from whatwg-url: https://github.com/jsdom/whatwg-url/tree/master/src -// The reference code bloat comes from Unicode issues with URLs, so those won't work here. -export class URLSearchParams { - _searchParams: Array> = []; - - constructor(params: any) { - if (typeof params === 'object') { - Object.keys(params).forEach(key => this.append(key, params[key])); - } - } - - append(key: string, value: string): void { - this._searchParams.push([key, value]); - } - - delete(name: string): void { - throw new Error('URLSearchParams.delete is not implemented'); - } - - get(name: string): void { - throw new Error('URLSearchParams.get is not implemented'); - } - - getAll(name: string): void { - throw new Error('URLSearchParams.getAll is not implemented'); - } - - has(name: string): void { - throw new Error('URLSearchParams.has is not implemented'); - } - - set(name: string, value: string): void { - throw new Error('URLSearchParams.set is not implemented'); - } - - sort(): void { - throw new Error('URLSearchParams.sort is not implemented'); - } - - // $FlowFixMe[unsupported-syntax] - // $FlowFixMe[missing-local-annot] - [Symbol.iterator]() { - return this._searchParams[Symbol.iterator](); - } - - toString(): string { - if (this._searchParams.length === 0) { - return ''; - } - const last = this._searchParams.length - 1; - return this._searchParams.reduce((acc, curr, index) => { - return ( - acc + - encodeURIComponent(curr[0]) + - '=' + - encodeURIComponent(curr[1]) + - (index === last ? '' : '&') - ); - }, ''); - } -} +export {URLSearchParams} from './URLSearchParams'; function validateBaseUrl(url: string) { // from this MIT-licensed gist: https://gist.github.com/dperini/729294 diff --git a/packages/react-native/Libraries/Blob/URLSearchParams.js b/packages/react-native/Libraries/Blob/URLSearchParams.js new file mode 100644 index 00000000000000..191912fc9cec86 --- /dev/null +++ b/packages/react-native/Libraries/Blob/URLSearchParams.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +// Small subset from whatwg-url: https://github.com/jsdom/whatwg-url/tree/master/src +// The reference code bloat comes from Unicode issues with URLs, so those won't work here. +export class URLSearchParams { + _searchParams: Array> = []; + + constructor(params: any) { + if (typeof params === 'object') { + Object.keys(params).forEach(key => this.append(key, params[key])); + } + } + + append(key: string, value: string): void { + this._searchParams.push([key, value]); + } + + delete(name: string): void { + throw new Error('URLSearchParams.delete is not implemented'); + } + + get(name: string): void { + throw new Error('URLSearchParams.get is not implemented'); + } + + getAll(name: string): void { + throw new Error('URLSearchParams.getAll is not implemented'); + } + + has(name: string): void { + throw new Error('URLSearchParams.has is not implemented'); + } + + set(name: string, value: string): void { + throw new Error('URLSearchParams.set is not implemented'); + } + + sort(): void { + throw new Error('URLSearchParams.sort is not implemented'); + } + + // $FlowFixMe[unsupported-syntax] + // $FlowFixMe[missing-local-annot] + [Symbol.iterator]() { + return this._searchParams[Symbol.iterator](); + } + + toString(): string { + if (this._searchParams.length === 0) { + return ''; + } + const last = this._searchParams.length - 1; + return this._searchParams.reduce((acc, curr, index) => { + return ( + acc + + encodeURIComponent(curr[0]) + + '=' + + encodeURIComponent(curr[1]) + + (index === last ? '' : '&') + ); + }, ''); + } +} diff --git a/packages/react-native/Libraries/Blob/URLSearchParams.js.flow b/packages/react-native/Libraries/Blob/URLSearchParams.js.flow new file mode 100644 index 00000000000000..22a98dfce6f405 --- /dev/null +++ b/packages/react-native/Libraries/Blob/URLSearchParams.js.flow @@ -0,0 +1,23 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +declare export class URLSearchParams { + _searchParams: Array>; + constructor(params: any): void; + append(key: string, value: string): void; + delete(name: string): void; + get(name: string): void; + getAll(name: string): void; + has(name: string): void; + set(name: string, value: string): void; + sort(): void; + @@iterator: Iterator>; + toString(): string; +} diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 71595d6a5f4ef9..a16589c774fbc7 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -1507,7 +1507,48 @@ declare export default typeof NativeFileReaderModule; " `; -exports[`public API should not change unintentionally Libraries/Blob/URL.js 1`] = `"UNTYPED MODULE (unsupported-syntax suppression)"`; +exports[`public API should not change unintentionally Libraries/Blob/URL.js 1`] = ` +"export { URLSearchParams } from \\"./URLSearchParams\\"; +declare export class URL { + _url: string; + _searchParamsInstance: ?URLSearchParams; + static createObjectURL(blob: Blob): string; + static revokeObjectURL(url: string): void; + constructor(url: string, base: string | URL): void; + get hash(): string; + get host(): string; + get hostname(): string; + get href(): string; + get origin(): string; + get password(): string; + get pathname(): string; + get port(): string; + get protocol(): string; + get search(): string; + get searchParams(): URLSearchParams; + toJSON(): string; + toString(): string; + get username(): string; +} +" +`; + +exports[`public API should not change unintentionally Libraries/Blob/URLSearchParams.js.flow 1`] = ` +"declare export class URLSearchParams { + _searchParams: Array>; + constructor(params: any): void; + append(key: string, value: string): void; + delete(name: string): void; + get(name: string): void; + getAll(name: string): void; + has(name: string): void; + set(name: string, value: string): void; + sort(): void; + @@iterator: Iterator>; + toString(): string; +} +" +`; exports[`public API should not change unintentionally Libraries/BugReporting/BugReporting.js 1`] = ` "type ExtraData = { [key: string]: string, ... }; diff --git a/packages/react-native/Libraries/__tests__/public-api-test.js b/packages/react-native/Libraries/__tests__/public-api-test.js index 19b04cfb53d92e..40b482d6b6438e 100644 --- a/packages/react-native/Libraries/__tests__/public-api-test.js +++ b/packages/react-native/Libraries/__tests__/public-api-test.js @@ -12,7 +12,7 @@ import type {TransformVisitor} from 'hermes-transform'; const translate = require('flow-api-translator'); -const {promises: fs} = require('fs'); +const {existsSync, promises: fs} = require('fs'); const glob = require('glob'); const {transform} = require('hermes-transform'); const path = require('path'); @@ -47,10 +47,22 @@ describe('public API', () => { const source = await fs.readFile(path.join(PACKAGE_ROOT, file), 'utf-8'); if (/@flow/.test(source)) { + // Require and use adjacent .js.flow file when source file includes an + // unsupported-syntax suppression if (source.includes('// $FlowFixMe[unsupported-syntax]')) { - expect( - 'UNTYPED MODULE (unsupported-syntax suppression)', - ).toMatchSnapshot(); + const flowDefPath = path.join( + PACKAGE_ROOT, + file.replace('.js', '.js.flow'), + ); + + if (!existsSync(flowDefPath)) { + throw new Error( + 'Found an unsupported-syntax suppression in ' + + file + + ', meaning types cannot be parsed. Add an adjacent .js.flow file to fix this!', + ); + } + return; }