Skip to content

Commit

Permalink
Break out URLSearchParams, require typedef for unparsable files (#45783)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #45783

Improves type strictness in the `react-native` package.

- Break out `URLSearchParams` from `URL.js` into its own module, to isolate a `$FlowFixMe[unsupported-syntax]` suppression within that definition.
- Update `public-api-test` to require an adjacent `<module>.js.flow` type definition file whenever a `$FlowFixMe[unsupported-syntax]`is present.
- Add `URLSearchParams.js.flow` with a Flow parser compatible typedef (`@iterator` instead of `[Symbol.iterator]`).

The result of these changes is to add missing typedef test coverage for `Libraries/Blob/URL.js` (see updated test snapshots).

Changelog: [Internal]

Reviewed By: hoxyq

Differential Revision: D60376327
  • Loading branch information
huntie authored and facebook-github-bot committed Aug 8, 2024
1 parent 5d70411 commit f59f3ab
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 67 deletions.
64 changes: 2 additions & 62 deletions packages/react-native/Libraries/Blob/URL.js
Original file line number Diff line number Diff line change
Expand Up @@ -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`:
*
Expand All @@ -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<Array<string>> = [];

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
Expand Down
71 changes: 71 additions & 0 deletions packages/react-native/Libraries/Blob/URLSearchParams.js
Original file line number Diff line number Diff line change
@@ -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<Array<string>> = [];

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 ? '' : '&')
);
}, '');
}
}
23 changes: 23 additions & 0 deletions packages/react-native/Libraries/Blob/URLSearchParams.js.flow
Original file line number Diff line number Diff line change
@@ -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<Array<string>>;
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<Array<string>>;
toString(): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Array<string>>;
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<Array<string>>;
toString(): string;
}
"
`;

exports[`public API should not change unintentionally Libraries/BugReporting/BugReporting.js 1`] = `
"type ExtraData = { [key: string]: string, ... };
Expand Down
20 changes: 16 additions & 4 deletions packages/react-native/Libraries/__tests__/public-api-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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 <module>.js.flow file to fix this!',
);
}

return;
}

Expand Down

0 comments on commit f59f3ab

Please sign in to comment.