diff --git a/.eslintrc.js b/.eslintrc.js index 1f6fd09f..7828c099 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,6 +41,7 @@ module.exports = { overrides: [ { files: './src/**/*', + extends: ['plugin:@typescript-eslint/recommended-requiring-type-checking'], rules: { 'prefer-rest-params': 'off', 'no-console': ['error'], @@ -92,6 +93,12 @@ module.exports = { }, ], + '@typescript-eslint/restrict-plus-operands': 0, + '@typescript-eslint/no-unsafe-argument': 0, + '@typescript-eslint/no-unsafe-call': 0, + '@typescript-eslint/no-unsafe-return': 0, + '@typescript-eslint/no-unsafe-assignment': 0, + '@typescript-eslint/no-unsafe-member-access': 0, '@typescript-eslint/no-explicit-any': ['warn'], '@typescript-eslint/prefer-optional-chain': 0, // ["warn"], @@ -117,6 +124,17 @@ module.exports = { rules: { 'prefer-const': ['error', { destructuring: 'all' }], 'import/no-default-export': 'error', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + destructuredArrayIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/restrict-template-expressions': 0, '@typescript-eslint/consistent-type-exports': 'error', '@typescript-eslint/prefer-includes': 'error', }, diff --git a/src/CredentialProvider.ts b/src/CredentialProvider.ts index ee1051df..99aaebb0 100644 --- a/src/CredentialProvider.ts +++ b/src/CredentialProvider.ts @@ -19,6 +19,7 @@ export class CredentialProvider { }) } + // eslint-disable-next-line @typescript-eslint/require-await async getCredentials(): Promise { return this.credentials.get() } diff --git a/src/client.ts b/src/client.ts index 77798a6d..64361ef5 100644 --- a/src/client.ts +++ b/src/client.ts @@ -270,7 +270,7 @@ export class Client { if (params.credentialsProvider) { this.credentialsProvider = params.credentialsProvider - this.checkAndRefreshCreds() + void this.checkAndRefreshCreds() } this.regionMap = {} @@ -659,7 +659,7 @@ export class Client { const regionPromise = region ? Promise.resolve(region) : this.getBucketRegionAsync(options.bucketName!) - this.checkAndRefreshCreds() + void this.checkAndRefreshCreds() return regionPromise.then( (finalRegion) => @@ -761,7 +761,7 @@ export class Client { if (isObject(region)) { // Backward Compatibility // makeBucket(bucketName: string, makeOpts: MakeBucketOpt, callback: NoResultCallback): void - makeOpts = region as MakeBucketOpt + makeOpts = region region = '' } @@ -1303,7 +1303,7 @@ export class Client { uploadId = previousUploadId } else { // there was no previous upload, initiate a new one - uploadId = (await this.initiateNewMultipartUpload(bucketName, objectName, metaData)) as string + uploadId = await this.initiateNewMultipartUpload(bucketName, objectName, metaData) } { @@ -1706,13 +1706,13 @@ export class Client { } }) if (result.isTruncated) { - listNext(result.nextKeyMarker, result.nextUploadIdMarker) + listNext(result.nextKeyMarker as string, result.nextUploadIdMarker as string) return } if (latestUpload) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - return resolve(latestUpload.uploadId) + return resolve(latestUpload.uploadId as string) } resolve(undefined) }) diff --git a/src/helpers.ts b/src/helpers.ts index bbf09225..f2190466 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -240,6 +240,7 @@ export function isObject(arg: unknown): arg is object { // check if object is readable stream export function isReadableStream(arg: unknown): arg is stream.Readable { + // eslint-disable-next-line @typescript-eslint/unbound-method return isObject(arg) && isFunction((arg as stream.Readable)._read) } diff --git a/src/typed-client.ts b/src/typed-client.ts index 2440005e..e65c6096 100644 --- a/src/typed-client.ts +++ b/src/typed-client.ts @@ -410,6 +410,7 @@ export class TypedClient extends Client { const readStream = new stream.Readable({ objectMode: true }) let marker = '' + // eslint-disable-next-line @typescript-eslint/no-misused-promises readStream._read = async () => { // push one object per _read() if (objects.length) { @@ -711,7 +712,7 @@ export class TypedClient extends Client { if (isFunction(removeOptsOrCallback)) { cb = removeOptsOrCallback } else { - removeOpts = removeOptsOrCallback as RemoveOptions + removeOpts = removeOptsOrCallback cb = callback } @@ -832,7 +833,7 @@ export class TypedClient extends Client { const region = await this.getBucketRegionAsync(bucketName) const reqOptions = this.getRequestOptions({ method, region, bucketName, objectName, query }) - this.checkAndRefreshCreds() + void this.checkAndRefreshCreds() return presignSignatureV4( reqOptions, this.accessKey, @@ -959,7 +960,7 @@ export class TypedClient extends Client { const region = await this.getBucketRegionAsync(postPolicy.formData.bucket) const date = new Date() const dateStr = makeDateLong(date) - this.checkAndRefreshCreds() + void this.checkAndRefreshCreds() if (!postPolicy.policy.expiration) { // 'expiration' is mandatory field for S3. @@ -1033,11 +1034,11 @@ export class TypedClient extends Client { throw new errors.InvalidBucketNameError('Invalid object name: ' + objectName) } - let [[tags, putOpts], cb] = findCallback(Array.from(arguments).slice(2)) as [ - [TagList, VersionIdentification?], - NoResultCallback, - ] - + let [[tags, putOpts], cb] = findCallback<[TagList, VersionIdentification?], NoResultCallback>([ + tagsArg, + putOptsArg, + cbArg, + ]) putOpts = putOpts ?? {} if (!isObject(tags)) { @@ -1135,11 +1136,7 @@ export class TypedClient extends Client { throw new errors.InvalidBucketNameError('Invalid object name: ' + objectName) } - const [[removeOpts], cb] = findCallback(Array.from(arguments).slice(2)) as [ - [VersionIdentification?], - NoResultCallback, - ] - + const [[removeOpts], cb] = findCallback<[VersionIdentification?], NoResultCallback>([removeOptsArg, cbArg]) if (removeOpts && Object.keys(removeOpts).length && !isObject(removeOpts)) { throw new errors.InvalidArgumentError('removeOpts should be of type "object"') } @@ -1304,10 +1301,10 @@ export class TypedClient extends Client { throw new errors.InvalidBucketNameError('Invalid object name: ' + objectName) } - const [[getOpts = {}], cb] = findCallback(Array.from(arguments).slice(2)) as [ - [VersionIdentification | undefined], - ResultCallback, - ] + const [[getOpts = {}], cb] = findCallback<[VersionIdentification | undefined], ResultCallback>([ + getOptsArg, + cbArg, + ]) if (!isObject(getOpts)) { throw new errors.InvalidArgumentError('getOpts should be of type "object"') @@ -1357,9 +1354,10 @@ export class TypedClient extends Client { throw new errors.InvalidObjectNameError(`Invalid object name: ${objectName}`) } - const [[getOpts = {}], cb] = findCallback<[VersionIdentification], ResultCallback>( - Array.from(arguments).slice(2), - ) + const [[getOpts = {}], cb] = findCallback<[VersionIdentification], ResultCallback>([ + getOptsArg, + cbArg, + ]) if (!isObject(getOpts)) { throw new TypeError('getOpts should be of type "Object"') @@ -1579,7 +1577,7 @@ export class TypedClient extends Client { } } -class Helper { +export class Helper { constructor(private readonly client: Client) {} async MultipleFileUpload( @@ -1637,7 +1635,7 @@ class Helper { uploadId = previousUploadId } else { // there was no previous upload, initiate a new one - uploadId = (await this.client.initiateNewMultipartUpload(bucketName, objectName, metaData)) as string + uploadId = await this.client.initiateNewMultipartUpload(bucketName, objectName, metaData) } { diff --git a/src/xml-parsers.ts b/src/xml-parsers.ts index 76be33ff..c1ae330e 100644 --- a/src/xml-parsers.ts +++ b/src/xml-parsers.ts @@ -287,7 +287,7 @@ export function parseInitiateMultipart(xml: string) { throw new errors.InvalidXMLError('Missing tag: "UploadId"') } -type MultipartResult = +export type MultipartResult = | { errCode: string; errMessage: string } | { errCode?: undefined // this help TS to narrow type @@ -321,17 +321,16 @@ export function parseCompleteMultipart(xml: string) { } } -const formatObjInfo = ( - content: { - Key: string - LastModified: string - ETag: string - Size: number - VersionId?: string - IsLatest?: boolean - }, - opts: { IsDeleteMarker?: boolean } = {}, -) => { +type ListedObject = { + Key: string + LastModified: string + ETag: string + Size: number + VersionId?: string + IsLatest?: boolean +} + +const formatObjInfo = (content: ListedObject, opts: { IsDeleteMarker?: boolean } = {}) => { const { Key, LastModified, ETag, Size, VersionId, IsLatest } = content if (!isObject(opts)) { @@ -383,7 +382,22 @@ export function parseListObjects(xml: string) { } let isTruncated = false let nextMarker, nextVersionKeyMarker - const xmlobj = parseXml(xml) + const xmlobj = parseXml(xml) as { + ListBucketResult?: { + CommonPrefixes: { Prefix: string } + IsTruncated: boolean + NextMarker?: string + Contents: Array<{ Key: string; LastModified: string; ETag: string; Size: number }> + } + ListVersionsResult?: { + CommonPrefixes: unknown + NextKeyMarker?: string + NextVersionIdMarker?: string + Version: Array + DeleteMarker?: Array + IsTruncated: boolean + } + } const parseCommonPrefixesEntity = (responseEntity: any) => { if (responseEntity) { @@ -393,7 +407,9 @@ export function parseListObjects(xml: string) { } } + // https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjects.html const listBucketResult = xmlobj.ListBucketResult + // https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectVersions.html const listVersionsResult = xmlobj.ListVersionsResult if (listBucketResult) { @@ -402,9 +418,9 @@ export function parseListObjects(xml: string) { } if (listBucketResult.Contents) { toArray(listBucketResult.Contents).forEach((content) => { - const name = sanitizeObjectKey(toArray(content.Key)[0]) - const lastModified = new Date(toArray(content.LastModified)[0]) - const etag = sanitizeETag(toArray(content.ETag)[0]) + const name = sanitizeObjectKey(content.Key) + const lastModified = new Date(content.LastModified) + const etag = sanitizeETag(content.ETag) const size = content.Size result.objects.push({ name, lastModified, etag, size }) }) @@ -755,7 +771,7 @@ export function parseSelectObjectContentResponse(res: Buffer): SelectResults { switch (messageType) { case 'error': { - const errorMessage = headers['error-code'] + ':"' + headers['error-message'] + '"' + const errorMessage = `${headers['error-code']}:"${headers['error-message']}"` throw new Error(errorMessage) } case 'event': {