Skip to content

Commit

Permalink
Use new TypeScript 3.7 features
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Nov 7, 2019
1 parent 505f639 commit 9f4fe33
Show file tree
Hide file tree
Showing 16 changed files with 112 additions and 103 deletions.
6 changes: 2 additions & 4 deletions package.json
Expand Up @@ -65,7 +65,7 @@
"create-test-server": "^3.0.1",
"del-cli": "^3.0.0",
"delay": "^4.3.0",
"eslint-config-xo-typescript": "^0.19.0",
"eslint-config-xo-typescript": "^0.21.0",
"form-data": "^3.0.0",
"get-port": "^5.0.0",
"keyv": "^3.1.0",
Expand Down Expand Up @@ -115,9 +115,7 @@
],
"rules": {
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/promise-function-async": "off",
"@typescript-eslint/strict-boolean-expressions": "off",
"@typescript-eslint/no-unnecessary-condition": "off"
"@typescript-eslint/promise-function-async": "off"
},
"ignores": [
"documentation/examples/*"
Expand Down
19 changes: 12 additions & 7 deletions source/as-promise.ts
Expand Up @@ -11,7 +11,7 @@ import requestAsEventEmitter from './request-as-event-emitter';

type ResponseReturn = Response | Buffer | string | any;

export const isProxiedSymbol = Symbol('proxied');
export const isProxiedSymbol: unique symbol = Symbol('proxied');

export default function asPromise(options: NormalizedOptions): CancelableRequest<Response> {
const proxy = new EventEmitter();
Expand Down Expand Up @@ -56,8 +56,8 @@ export default function asPromise(options: NormalizedOptions): CancelableRequest
return;
}

if (response.req && response.req.aborted) {
// Canceled while downloading - will throw a CancelError or TimeoutError
if (response.req?.aborted) {
// Canceled while downloading - will throw a `CancelError` or `TimeoutError` error
return;
}

Expand Down Expand Up @@ -123,14 +123,19 @@ export default function asPromise(options: NormalizedOptions): CancelableRequest
});

emitter.once('error', reject);
[

const events = [
'request',
'redirect',
'uploadProgress',
'downloadProgress'
].forEach(event => emitter.on(event, (...args: unknown[]) => {
proxy.emit(event, ...args);
}));
];

for (const event of events) {
emitter.on(event, (...args: unknown[]) => {
proxy.emit(event, ...args);
});
}
}) as CancelableRequest<ResponseReturn>;

promise[isProxiedSymbol] = true;
Expand Down
4 changes: 2 additions & 2 deletions source/as-stream.ts
Expand Up @@ -82,8 +82,8 @@ export default function asStream(options: NormalizedOptions): ProxyStream {
for (const [key, value] of Object.entries(response.headers)) {
// Got gives *decompressed* data. Overriding `content-encoding` header would result in an error.
// It's not possible to decompress already decompressed data, is it?
const allowed = options.decompress ? key !== 'content-encoding' : true;
if (allowed) {
const isAllowed = options.decompress ? key !== 'content-encoding' : true;
if (isAllowed) {
destination.setHeader(key, value);
}
}
Expand Down
3 changes: 2 additions & 1 deletion source/calculate-retry-delay.ts
Expand Up @@ -16,6 +16,7 @@ const calculateRetryDelay: RetryFunction = ({attemptCount, retryOptions, error})
return 0;
}

// TODO: This type coercion is not entirely correct as it makes `response` a guaranteed property, when it's in fact not.
const {response} = error as HTTPError | ParseError | MaxRedirectsError;
if (response && Reflect.has(response.headers, 'retry-after') && retryAfterStatusCodes.has(response.statusCode)) {
let after = Number(response.headers['retry-after']);
Expand All @@ -32,7 +33,7 @@ const calculateRetryDelay: RetryFunction = ({attemptCount, retryOptions, error})
return after;
}

if (response && response.statusCode === 413) {
if (response?.statusCode === 413) {
return 0;
}

Expand Down
26 changes: 15 additions & 11 deletions source/create.ts
Expand Up @@ -18,10 +18,16 @@ import asStream, {ProxyStream} from './as-stream';
import {preNormalizeArguments, normalizeArguments} from './normalize-arguments';
import {Hooks} from './known-hook-events';

export type HTTPAlias = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';

export type ReturnResponse = (url: URLArgument | Options & { stream?: false; url: URLArgument }, options?: Options & { stream?: false }) => CancelableRequest<Response>;
export type ReturnStream = (url: URLArgument | Options & { stream: true; url: URLArgument }, options?: Options & { stream: true }) => ProxyStream;
export type HTTPAlias =
| 'get'
| 'post'
| 'put'
| 'patch'
| 'head'
| 'delete';

export type ReturnResponse = (url: URLArgument | Options & {stream?: false; url: URLArgument}, options?: Options & {stream?: false}) => CancelableRequest<Response>;
export type ReturnStream = (url: URLArgument | Options & {stream: true; url: URLArgument}, options?: Options & {stream: true}) => ProxyStream;
export type GotReturn = ProxyStream | CancelableRequest<Response>;

const getPromiseOrStream = (options: NormalizedOptions): GotReturn => options.stream ? asStream(options) : asPromise(options);
Expand All @@ -40,13 +46,13 @@ export interface Got extends Record<HTTPAlias, ReturnResponse> {
TimeoutError: typeof errors.TimeoutError;
CancelError: typeof errors.CancelError;

(url: URLArgument | Options & { stream?: false; url: URLArgument }, options?: Options & { stream?: false }): CancelableRequest<Response>;
(url: URLArgument | Options & { stream: true; url: URLArgument }, options?: Options & { stream: true }): ProxyStream;
(url: URLArgument | Options & {stream?: false; url: URLArgument}, options?: Options & {stream?: false}): CancelableRequest<Response>;
(url: URLArgument | Options & {stream: true; url: URLArgument}, options?: Options & {stream: true}): ProxyStream;
(url: URLOrOptions, options?: Options): CancelableRequest<Response> | ProxyStream;
create(defaults: Defaults): Got;
extend(...instancesOrOptions: Array<Got | ExtendedOptions>): Got;
mergeInstances(parent: Got, ...instances: Got[]): Got;
mergeOptions<T extends Options>(...sources: T[]): T & { hooks: Partial<Hooks> };
mergeOptions<T extends Options>(...sources: T[]): T & {hooks: Partial<Hooks>};
}

export interface GotStream extends Record<HTTPAlias, ReturnStream> {
Expand Down Expand Up @@ -76,7 +82,7 @@ const create = (nonNormalizedDefaults: Defaults): Got => {

// @ts-ignore Because the for loop handles it for us, as well as the other Object.defines
const got: Got = (url: URLOrOptions, options?: Options): GotReturn => {
const isStream = options && options.stream;
const isStream = options?.stream ?? false;

let iteration = 0;
const iterateHandlers = (newOptions: NormalizedOptions): GotReturn => {
Expand All @@ -97,9 +103,7 @@ const create = (nonNormalizedDefaults: Defaults): Got => {
if (!isStream && !Reflect.has(result, isProxiedSymbol)) {
for (const key of Object.keys(nextPromise)) {
Object.defineProperty(result, key, {
get: () => {
return nextPromise[key];
},
get: () => nextPromise[key],
set: (value: unknown) => {
nextPromise[key] = value;
}
Expand Down
10 changes: 4 additions & 6 deletions source/errors.ts
Expand Up @@ -6,8 +6,7 @@ import {TimeoutError as TimedOutError} from './utils/timed-out';

export class GotError extends Error {
code?: string;

readonly options!: NormalizedOptions;
readonly options: NormalizedOptions;

constructor(message: string, error: (Error & {code?: string}) | {code?: string}, options: NormalizedOptions) {
super(message);
Expand Down Expand Up @@ -46,7 +45,7 @@ export class ReadError extends GotError {
}

export class ParseError extends GotError {
readonly response!: Response;
readonly response: Response;

constructor(error: Error, response: Response, options: NormalizedOptions) {
super(`${error.message} in "${format(options as unknown as URL)}"`, error, options);
Expand All @@ -59,7 +58,7 @@ export class ParseError extends GotError {
}

export class HTTPError extends GotError {
readonly response!: Response;
readonly response: Response;

constructor(response: Response, options: NormalizedOptions) {
const {statusCode, statusMessage} = response;
Expand All @@ -74,7 +73,7 @@ export class HTTPError extends GotError {
}

export class MaxRedirectsError extends GotError {
readonly response!: Response;
readonly response: Response;

constructor(response: Response, maxRedirects: number, options: NormalizedOptions) {
super(`Redirected ${maxRedirects} times. Aborting.`, {}, options);
Expand All @@ -95,7 +94,6 @@ export class UnsupportedProtocolError extends GotError {

export class TimeoutError extends GotError {
timings: Timings;

event: string;

constructor(error: TimedOutError, timings: Timings, options: NormalizedOptions) {
Expand Down
2 changes: 1 addition & 1 deletion source/get-response.ts
Expand Up @@ -19,7 +19,7 @@ export default (response: IncomingMessage, options: NormalizedOptions, emitter:
options.method !== 'HEAD' ? decompressResponse(progressStream as unknown as IncomingMessage) : progressStream
) as Response;

if (!options.decompress && ['gzip', 'deflate', 'br'].includes(response.headers['content-encoding'] || '')) {
if (!options.decompress && ['gzip', 'deflate', 'br'].includes(response.headers['content-encoding'] ?? '')) {
options.encoding = null;
}

Expand Down
48 changes: 25 additions & 23 deletions source/merge.ts
Expand Up @@ -67,34 +67,36 @@ export function mergeOptions(...sources: Array<Partial<Options>>): Partial<Optio

for (const source of sources) {
// We need to check `source` to allow calling `.extend()` with no arguments.
if (source) {
if (Reflect.has(source, 'hooks')) {
for (const hook of knownHookEvents) {
hooks[hook] = hooks[hook].concat(source.hooks[hook] || []);
}
}
if (!source) {
continue;
}

if (Reflect.has(source, 'context')) {
Object.defineProperty(mergedOptions, 'context', {
writable: true,
configurable: true,
enumerable: false,
// @ts-ignore
value: source.context
});
if (Reflect.has(source, 'hooks')) {
for (const hook of knownHookEvents) {
hooks[hook] = hooks[hook].concat(source.hooks[hook] ?? []);
}
}

if (Reflect.has(source, 'body')) {
mergedOptions.body = source.body;
}
if (Reflect.has(source, 'context')) {
Object.defineProperty(mergedOptions, 'context', {
writable: true,
configurable: true,
enumerable: false,
// @ts-ignore
value: source.context
});
}

if (Reflect.has(source, 'json')) {
mergedOptions.json = source.json;
}
if (Reflect.has(source, 'body')) {
mergedOptions.body = source.body;
}

if (Reflect.has(source, 'form')) {
mergedOptions.form = source.form;
}
if (Reflect.has(source, 'json')) {
mergedOptions.json = source.json;
}

if (Reflect.has(source, 'form')) {
mergedOptions.form = source.form;
}
}

Expand Down
5 changes: 3 additions & 2 deletions source/normalize-arguments.ts
Expand Up @@ -119,7 +119,7 @@ export const normalizeArguments = (url: URLOrOptions, options: Options, defaults
let urlArgument: URLArgument;
if (is.plainObject(url)) {
options = {...url as Options, ...options};
urlArgument = options.url || {};
urlArgument = options.url ?? {};
delete options.url;
} else {
urlArgument = url;
Expand Down Expand Up @@ -200,6 +200,7 @@ export const normalizeArguments = (url: URLOrOptions, options: Options, defaults
if (is.nonEmptyString(searchParams) || is.nonEmptyObject(searchParams) || (searchParams && searchParams instanceof URLSearchParams)) {
if (!is.string(searchParams)) {
if (!(searchParams instanceof URLSearchParams)) {
// @ts-ignore
validateSearchParams(searchParams);
}

Expand All @@ -212,7 +213,7 @@ export const normalizeArguments = (url: URLOrOptions, options: Options, defaults
if (options.hostname === 'unix') {
const matches = /(?<socketPath>.+?):(?<path>.+)/.exec(options.path);

if (matches && matches.groups) {
if (matches?.groups) {
const {socketPath, path} = matches.groups;
options = {
...options,
Expand Down
24 changes: 12 additions & 12 deletions source/progress.ts
Expand Up @@ -4,19 +4,19 @@ import {Socket} from 'net';
import EventEmitter = require('events');

export function downloadProgress(_response: IncomingMessage, emitter: EventEmitter, downloadBodySize?: number): TransformStream {
let downloaded = 0;
let downloadedBytes = 0;

return new TransformStream({
transform(chunk, _encoding, callback) {
downloaded += chunk.length;
downloadedBytes += chunk.length;

const percent = downloadBodySize ? downloaded / downloadBodySize : 0;
const percent = downloadBodySize ? downloadedBytes / downloadBodySize : 0;

// Let `flush()` be responsible for emitting the last event
if (percent < 1) {
emitter.emit('downloadProgress', {
percent,
transferred: downloaded,
transferred: downloadedBytes,
total: downloadBodySize
});
}
Expand All @@ -27,7 +27,7 @@ export function downloadProgress(_response: IncomingMessage, emitter: EventEmitt
flush(callback) {
emitter.emit('downloadProgress', {
percent: 1,
transferred: downloaded,
transferred: downloadedBytes,
total: downloadBodySize
});

Expand All @@ -38,7 +38,7 @@ export function downloadProgress(_response: IncomingMessage, emitter: EventEmitt

export function uploadProgress(request: ClientRequest, emitter: EventEmitter, uploadBodySize?: number): void {
const uploadEventFrequency = 150;
let uploaded = 0;
let uploadedBytes = 0;
let progressInterval: NodeJS.Timeout;

emitter.emit('uploadProgress', {
Expand All @@ -56,29 +56,29 @@ export function uploadProgress(request: ClientRequest, emitter: EventEmitter, up

emitter.emit('uploadProgress', {
percent: 1,
transferred: uploaded,
transferred: uploadedBytes,
total: uploadBodySize
});
});

request.once('socket', (socket: Socket) => {
const onSocketConnect = (): void => {
progressInterval = setInterval(() => {
const lastUploaded = uploaded;
const lastUploadedBytes = uploadedBytes;
/* istanbul ignore next: see #490 (occurs randomly!) */
const headersSize = (request as any)._header ? Buffer.byteLength((request as any)._header) : 0;
uploaded = socket.bytesWritten - headersSize;
uploadedBytes = socket.bytesWritten - headersSize;

// Don't emit events with unchanged progress and
// prevent last event from being emitted, because
// it's emitted when `response` is emitted
if (uploaded === lastUploaded || uploaded === uploadBodySize) {
if (uploadedBytes === lastUploadedBytes || uploadedBytes === uploadBodySize) {
return;
}

emitter.emit('uploadProgress', {
percent: uploadBodySize ? uploaded / uploadBodySize : 0,
transferred: uploaded,
percent: uploadBodySize ? uploadedBytes / uploadBodySize : 0,
transferred: uploadedBytes,
total: uploadBodySize
});
}, uploadEventFrequency);
Expand Down

0 comments on commit 9f4fe33

Please sign in to comment.