From 6a0033ef4d7aca36ea63b792a49d6981a26b5036 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 8 Jun 2024 18:56:42 +0800 Subject: [PATCH 1/2] feat: support cjs and esm both https://github.com/eggjs/egg/issues/5257 --- .github/workflows/node.js.yml | 5 +- .gitignore | 1 + .mailmap | 1 - Readme.md | 17 ++--- index.ts | 3 - package.json | 94 +++++++++++++++++-------- src/application.ts | 32 ++++----- src/context.ts | 69 +++++++++++++----- src/index.ts | 9 +++ src/request.ts | 29 ++++---- src/response.ts | 48 +++++++------ src/types.ts | 4 +- {lib => test}/application.test-d.ts | 4 +- test/application/context.test.ts | 2 +- test/application/currentContext.test.ts | 2 +- test/application/index.test.ts | 6 +- test/application/inspect.test.ts | 5 +- test/application/onerror.test.ts | 2 +- test/application/request.test.ts | 2 +- test/application/respond.test.ts | 20 +++--- test/application/response.test.ts | 2 +- test/application/toJSON.test.ts | 2 +- test/application/use.test.ts | 4 +- test/context/assert.test.ts | 2 +- test/context/cookies.test.ts | 31 +++++--- test/context/inspect.test.ts | 2 +- test/context/onerror.test.ts | 4 +- test/context/state.test.ts | 2 +- test/context/throw.test.ts | 56 +++++++++++---- test/context/toJSON.test.ts | 2 +- test/request/accept.test.ts | 2 +- test/request/accepts.test.ts | 2 +- test/request/acceptsCharsets.test.ts | 2 +- test/request/acceptsEncodings.test.ts | 2 +- test/request/acceptsLanguages.test.ts | 2 +- test/request/charset.test.ts | 2 +- test/request/fresh.test.ts | 2 +- test/request/get.test.ts | 2 +- test/request/header.test.ts | 2 +- test/request/headers.test.ts | 2 +- test/request/host.test.ts | 2 +- test/request/hostname.test.ts | 2 +- test/request/href.test.ts | 4 +- test/request/idempotent.test.ts | 2 +- test/request/inspect.test.ts | 2 +- test/request/ip.test.ts | 6 +- test/request/ips.test.ts | 2 +- test/request/is.test.ts | 2 +- test/request/length.test.ts | 2 +- test/request/origin.test.ts | 2 +- test/request/path.test.ts | 4 +- test/request/protocol.test.ts | 2 +- test/request/query.test.ts | 2 +- test/request/querystring.test.ts | 4 +- test/request/search.test.ts | 2 +- test/request/secure.test.ts | 2 +- test/request/stale.test.ts | 2 +- test/request/subdomains.test.ts | 2 +- test/request/type.test.ts | 2 +- test/request/whatwg-url.test.ts | 2 +- test/response/append.test.ts | 2 +- test/response/attachment.test.ts | 4 +- test/response/body.test.ts | 2 +- test/response/etag.test.ts | 2 +- test/response/flushHeaders.test.ts | 4 +- test/response/has.test.ts | 2 +- test/response/header.test.ts | 4 +- test/response/headers.test.ts | 2 +- test/response/inspect.test.ts | 2 +- test/response/is.test.ts | 2 +- test/response/last-modified.test.ts | 2 +- test/response/length.test.ts | 2 +- test/response/message.test.ts | 4 +- test/response/redirect.test.ts | 6 +- test/response/remove.test.ts | 2 +- test/response/set.test.ts | 2 +- test/response/socket.test.ts | 2 +- test/response/status.test.ts | 8 +-- test/response/type.test.ts | 2 +- test/response/vary.test.ts | 2 +- test/response/writable.test.ts | 15 ++-- test/test-helpers/context.ts | 4 +- tsconfig.json | 16 ++--- 83 files changed, 369 insertions(+), 256 deletions(-) delete mode 100644 .mailmap delete mode 100644 index.ts create mode 100644 src/index.ts rename {lib => test}/application.test-d.ts (72%) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index a62f5556a..5f903c9cd 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -3,7 +3,6 @@ name: Node.js CI on: push: branches: [ master ] - pull_request: branches: [ master ] @@ -13,4 +12,6 @@ jobs: uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: os: 'ubuntu-latest, macos-latest, windows-latest' - version: '16.13.0, 16, 18, 20, 21' + version: '18, 20, 22' + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 7e7b04ea0..f1f15ec30 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ dist lib !lib/application.test-d.ts package-lock.json +.tshy* diff --git a/.mailmap b/.mailmap deleted file mode 100644 index 1f2f86feb..000000000 --- a/.mailmap +++ /dev/null @@ -1 +0,0 @@ -Michał Gołębiowski-Owczarek diff --git a/Readme.md b/Readme.md index 9da3466bb..199d7c2a6 100644 --- a/Readme.md +++ b/Readme.md @@ -1,6 +1,6 @@ # @eggjs/koa -@eggjs/koa is forked from [Koa v2.x](https://github.com/koajs/koa/tree/v2.x) for LTS and drop Node.js < 16.13.0 support. +@eggjs/koa is forked from [Koa v2.x](https://github.com/koajs/koa/tree/v2.x) for LTS and drop Node.js < 18.7.0 support. Koa middleware framework for nodejs @@ -19,7 +19,7 @@ Koa is not bundled with any middleware. ## Installation -@eggjs/koa requires __node v16.3.0__ or higher for Node.js LTS support. +@eggjs/koa requires __node v18.7.0__ or higher for Node.js LTS support. ```bash npm install @eggjs/koa @@ -28,11 +28,12 @@ npm install @eggjs/koa ## Hello Koa ```ts -const Koa = require('@eggjs/koa'); -const app = new Koa(); +import { Application } from '@eggjs/koa'; + +const app = new Application(); // response -app.use(ctx => { +app.use((ctx) => { ctx.body = 'Hello Koa'; }); @@ -86,7 +87,7 @@ Each middleware receives a Koa `Context` object that encapsulates an incoming http message and the corresponding response to that message. `ctx` is often used as the parameter name for the context object. -```js +```ts app.use(async (ctx, next) => { await next(); }); @@ -180,6 +181,6 @@ See [AUTHORS](AUTHORS). - [Wiki](https://github.com/koajs/koa/wiki) - [中文文档 v2.x](https://github.com/demopark/koa-docs-Zh-CN) -# License +## License -[MIT](https://github.com/eggjs/koa/blob/master/LICENSE) +[MIT](LICENSE) diff --git a/index.ts b/index.ts deleted file mode 100644 index a567fe7d8..000000000 --- a/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import Application from './src/application'; - -export default Application; diff --git a/package.json b/package.json index b903c3280..bb53014e9 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,19 @@ { "name": "@eggjs/koa", "version": "2.17.0", - "description": "Koa web app framework", - "main": "lib/application.js", - "types": "lib/application.d.ts", - "files": [ - "lib/*.d.ts", - "lib/*.js" - ], + "engines": { + "node": ">= 18.7.0" + }, + "publishConfig": { + "access": "public" + }, + "description": "Koa web app framework for https://eggjs.org", "scripts": { - "test": "egg-bin test", - "ci": "egg-bin cov && npm run tsd", + "test": "npm run lint -- --fix && egg-bin test", + "ci": "npm run lint && egg-bin cov && npm run prepublishOnly", "lint": "eslint src test", - "tsd": "npm run tsc && tsd", "authors": "git log --format='%aN <%aE>' | sort -u > AUTHORS", - "clean": "tsc -b --clean", - "tsc": "tsc", - "prepublishOnly": "npm run tsc" + "prepublishOnly": "tshy && tshy-after" }, "repository": { "type": "git", @@ -34,9 +31,9 @@ "license": "MIT", "dependencies": { "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", + "cache-content-type": "^2.0.0", + "content-disposition": "~0.5.4", + "content-type": "^1.0.5", "cookies": "~0.8.0", "delegates": "^1.0.0", "destroy": "^1.0.4", @@ -45,32 +42,69 @@ "fresh": "~0.5.2", "gals": "^1.0.1", "http-assert": "^1.3.0", - "http-errors": "^1.6.3", - "is-generator-function": "^1.0.7", + "http-errors": "^2.0.0", + "is-type-of": "^2.1.0", "koa-compose": "^4.1.0", - "on-finished": "^2.3.0", - "only": "~0.0.2", - "parseurl": "^1.3.2", - "statuses": "^1.5.0", - "type-is": "^1.6.16", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1", + "type-is": "^1.6.18", "vary": "^1.1.2" }, "devDependencies": { "@eggjs/tsconfig": "^1.3.3", + "@types/content-type": "^1.1.8", + "@types/delegates": "^1.0.3", + "@types/destroy": "^1.0.3", + "@types/encodeurl": "^1.0.2", + "@types/escape-html": "^1.0.4", + "@types/fresh": "^0.5.2", + "@types/http-errors": "^2.0.4", + "@types/koa-compose": "^3.2.8", "@types/mocha": "^10.0.1", "@types/node": "^20.2.5", + "@types/on-finished": "^2.3.4", + "@types/parseurl": "^1.3.3", + "@types/statuses": "^2.0.5", + "@types/supertest": "^6.0.2", + "@types/type-is": "^1.6.6", + "@types/vary": "^1.1.3", "egg-bin": "^6.4.0", "eslint": "^8.41.0", "eslint-config-egg": "^13.1.0", "mm": "^3.3.0", "supertest": "^3.1.0", - "tsd": "^0.28.1", - "typescript": "^5.0.4" + "tsd": "^0.31.0", + "tshy": "^1.15.1", + "tshy-after": "^1.0.0", + "typescript": "^5.4.5" }, - "engines": { - "node": ">= 16.13.0" + "type": "module", + "tshy": { + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } }, - "publishConfig": { - "access": "public" - } + "exports": { + "./package.json": "./package.json", + ".": { + "import": { + "source": "./src/index.ts", + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "source": "./src/index.ts", + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + } + }, + "files": [ + "dist", + "src" + ], + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts" } diff --git a/src/application.ts b/src/application.ts index 568726be7..166ecb8fe 100644 --- a/src/application.ts +++ b/src/application.ts @@ -6,25 +6,23 @@ import http from 'node:http'; import type { AsyncLocalStorage } from 'node:async_hooks'; import type { IncomingMessage, ServerResponse } from 'node:http'; import { getAsyncLocalStorage } from 'gals'; -import isGeneratorFunction from 'is-generator-function'; +import { isGeneratorFunction } from 'is-type-of'; import onFinished from 'on-finished'; import statuses from 'statuses'; import compose from 'koa-compose'; -import only from 'only'; import { HttpError } from 'http-errors'; -import Context from './context'; -import Request from './request'; -import Response from './response'; -import type { ContextDelegation } from './context'; -import type { CustomError, ProtoImplClass, AnyProto } from './types'; +import Context from './context.js'; +import Request from './request.js'; +import Response from './response.js'; +import type { ContextDelegation } from './context.js'; +import type { CustomError, AnyProto } from './types.js'; const debug = debuglog('koa:application'); +export type ProtoImplClass = new(...args: any[]) => T; export type Next = () => Promise; export type MiddlewareFunc = (ctx: ContextDelegation, next: Next) => Promise | void; - -export type { ContextDelegation as Context } from './context'; -export type { CustomError, ProtoImplClass } from './types'; +export type { ContextDelegation as Context } from './context.js'; /** * Expose `Application` class. @@ -108,11 +106,11 @@ export default class Application extends Emitter { * We only bother showing settings. */ toJSON() { - return only(this, [ - 'subdomainOffset', - 'proxy', - 'env', - ]); + return { + subdomainOffset: this.subdomainOffset, + proxy: this.proxy, + env: this.env, + }; } /** @@ -165,7 +163,7 @@ export default class Application extends Emitter { } /** - * return currnect contenxt from async local storage + * return current context from async local storage */ get currentContext() { if (this.ctxStorage) return this.ctxStorage.getStore(); @@ -178,7 +176,7 @@ export default class Application extends Emitter { async #handleRequest(ctx: ContextDelegation, fnMiddleware: (ctx: ContextDelegation) => Promise) { const res = ctx.res; res.statusCode = 404; - const onerror = (err: Error) => ctx.onerror(err); + const onerror = (err: any) => ctx.onerror(err); onFinished(res, onerror); try { await fnMiddleware(ctx); diff --git a/src/context.ts b/src/context.ts index dfbd8127b..630a7a160 100644 --- a/src/context.ts +++ b/src/context.ts @@ -5,10 +5,10 @@ import httpAssert from 'http-assert'; import delegate from 'delegates'; import statuses from 'statuses'; import Cookies from 'cookies'; -import type Application from './application'; -import type Request from './request'; -import type Response from './response'; -import type { CustomError, AnyProto } from './types'; +import type Application from './application.js'; +import type Request from './request.js'; +import type Response from './response.js'; +import type { CustomError, AnyProto } from './types.js'; export default class Context { app: Application; @@ -74,15 +74,17 @@ export default class Context { * this.assert(this.user, 401, 'Please login!'); * * See: https://github.com/jshttp/http-assert - * - * @param {Mixed} test + * @param {Mixed} value * @param {Number} status - * @param {String} message - * @public + * @param {String} opts */ - - assert(...args: any[]) { - return httpAssert(...args); + assert(value: any, status?: number, opts?: Record): void; + assert(value: any, status?: number, msg?: string, opts?: Record): void; + assert(value: any, status?: number, msgOrOptions?: string | Record, opts?: Record) { + if (typeof msgOrOptions === 'string') { + return httpAssert(value, status, msgOrOptions, opts); + } + return httpAssert(value, status, msgOrOptions); } /** @@ -95,17 +97,48 @@ export default class Context { * this.throw('something exploded') * this.throw(new Error('invalid')) * this.throw(400, new Error('invalid')) + * this.throw(400, new Error('invalid'), { foo: 'bar' }) + * this.throw(new Error('invalid'), { foo: 'bar' }) * * See: https://github.com/jshttp/http-errors * * Note: `status` should only be passed as the first parameter. * - * @param {String|Number|Error} err, msg or status - * @param {String|Number|Error} [err, msg or status] - * @param {Object} [props] + * @param {String|Number|Error} status error, msg or status + * @param {String|Number|Error|Object} [error] error, msg, status or errorProps + * @param {Object} [errorProps] error object properties */ - throw(...args: any[]) { + throw(status: number): void; + throw(status: number, errorProps: object): void; + throw(status: number, errorMessage: string): void; + throw(status: number, errorMessage: string, errorProps: object): void; + throw(status: number, error: Error): void; + throw(status: number, error: Error, errorProps: object): void; + throw(errorMessage: string): void; + throw(errorMessage: string, errorProps: object): void; + throw(errorMessage: string, status: number): void; + throw(errorMessage: string, status: number, errorProps: object): void; + throw(error: Error): void; + throw(error: Error, errorProps: object): void; + throw(error: Error, status: number): void; + throw(error: Error, status: number, errorProps: object): void; + throw(arg1: number | string | Error, arg2?: number | string | Error | object, errorProps?: object) { + const args: any[] = []; + if (typeof arg2 === 'number') { + // throw(error, status) + args.push(arg2); + args.push(arg1); + } else { + // throw(status, error?) + args.push(arg1); + if (arg2) { + args.push(arg2); + } + } + if (errorProps) { + args.push(errorProps); + } throw createError(...args); } @@ -158,11 +191,11 @@ export default class Context { if (err.code === 'ENOENT') statusCode = 404; // default to 500 - if (typeof statusCode !== 'number' || !statuses[statusCode]) statusCode = 500; + if (typeof statusCode !== 'number' || !statuses.message[statusCode]) statusCode = 500; // respond - const code = statuses[statusCode]; - const msg = err.expose ? err.message : code; + const statusMessage = statuses.message[statusCode] as string; + const msg = err.expose ? err.message : statusMessage; this.response.status = err.status = statusCode; this.response.length = Buffer.byteLength(msg); res.end(msg); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..c8e22d87e --- /dev/null +++ b/src/index.ts @@ -0,0 +1,9 @@ +import Application from './application.js'; + +export default Application; + +export * from './application.js'; +export * from './context.js'; +export * from './request.js'; +export * from './response.js'; +export * from './types.js'; diff --git a/src/request.ts b/src/request.ts index f4e7be2ac..4b3e67cb1 100644 --- a/src/request.ts +++ b/src/request.ts @@ -10,10 +10,9 @@ import contentType from 'content-type'; import parse from 'parseurl'; import typeis from 'type-is'; import fresh from 'fresh'; -import only from 'only'; -import type Application from './application'; -import type Context from './context'; -import type Response from './response'; +import type Application from './application.js'; +import type Context from './context.js'; +import type Response from './response.js'; export default class Request { app: Application; @@ -118,7 +117,7 @@ export default class Request { */ get path() { - return parse(this.req).pathname as string; + return parse(this.req)!.pathname as string; } /** @@ -126,7 +125,7 @@ export default class Request { */ set path(path) { - const url = parse(this.req); + const url = parse(this.req)!; if (url.pathname === path) return; url.pathname = path; @@ -166,7 +165,7 @@ export default class Request { get querystring() { if (!this.req) return ''; - return parse(this.req).query || ''; + return parse(this.req)!.query as string || ''; } /** @@ -174,7 +173,7 @@ export default class Request { */ set querystring(str) { - const url = parse(this.req); + const url = parse(this.req)!; if (url.search === `?${str}`) return; url.search = str; @@ -510,7 +509,9 @@ export default class Request { * this.is('html'); // => false */ is(type?: string | string[], ...types: string[]): string | false | null { - return typeis(this.req, type, ...types); + const testTypes: string[] = Array.isArray(type) ? type : + (type ? [ type ] : []); + return typeis(this.req, [ ...testTypes, ...types ]); } /** @@ -570,10 +571,10 @@ export default class Request { * Return JSON representation. */ toJSON() { - return only(this, [ - 'method', - 'url', - 'header', - ]); + return { + method: this.method, + url: this.url, + header: this.header, + }; } } diff --git a/src/response.ts b/src/response.ts index 3384dbdf2..d8ffbee10 100644 --- a/src/response.ts +++ b/src/response.ts @@ -11,11 +11,10 @@ import { is as typeis } from 'type-is'; import statuses from 'statuses'; import destroy from 'destroy'; import vary from 'vary'; -import only from 'only'; import encodeUrl from 'encodeurl'; -import type Application from './application'; -import type { ContextDelegation } from './context'; -import type Request from './request'; +import type Application from './application.js'; +import type { ContextDelegation } from './context.js'; +import type Request from './request.js'; export default class Response { app: Application; @@ -70,7 +69,9 @@ export default class Response { assert(code >= 100 && code <= 999, `invalid status code: ${code}`); this._explicitStatus = true; this.res.statusCode = code; - if (this.req.httpVersionMajor < 2) this.res.statusMessage = statuses[code]; + if (this.req.httpVersionMajor < 2) { + this.res.statusMessage = statuses.message[code]!; + } if (this.body && statuses.empty[code]) this.body = null; } @@ -78,7 +79,7 @@ export default class Response { * Get response status message */ get message(): string { - return this.res.statusMessage || statuses[this.status]; + return this.res.statusMessage || statuses.message[this.status]!; } /** @@ -254,11 +255,13 @@ export default class Response { * this.type = 'png'; */ set type(type: string | null | undefined) { - type = getType(type); - if (type) { - this.set('Content-Type', type); - } else { + if (!type) { this.remove('Content-Type'); + return; + } + const mimeType = getType(type); + if (mimeType) { + this.set('Content-Type', mimeType); } } @@ -275,9 +278,14 @@ export default class Response { /** * Check whether the response is one of the listed types. * Pretty much the same as `this.request.is()`. + * + * this.response.is('html') + * this.response.is('html', 'json') */ is(type?: string | string[], ...types: string[]): string | false { - return typeis(this.type, type, ...types); + const testTypes: string[] = Array.isArray(type) ? type : + (type ? [ type ] : []); + return typeis(this.type as string, [ ...testTypes, ...types ]); } /** @@ -305,8 +313,8 @@ export default class Response { * Set the ETag of a response. * This will normalize the quotes if necessary. * - * this.response.etag = 'md5hashsum'; - * this.response.etag = '"md5hashsum"'; + * this.response.etag = 'md5-hash-sum'; + * this.response.etag = '"md5-hash-sum"'; * this.response.etag = 'W/"123456789"'; */ set etag(val: string) { @@ -362,7 +370,7 @@ export default class Response { * this.set('Accept', 'application/json'); * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); */ - set(field: string | object, val?: string | number | any[]) { + set(field: string | Record, val?: string | number | any[]) { if (this.headerSent) return; if (typeof field === 'string') { if (Array.isArray(val)) { @@ -437,7 +445,7 @@ export default class Response { inspect() { if (!this.res) return; const o = this.toJSON(); - o.body = this.body; + Reflect.set(o, 'body', this.body); return o; } @@ -449,11 +457,11 @@ export default class Response { * Return JSON representation. */ toJSON() { - return only(this, [ - 'status', - 'message', - 'header', - ]); + return { + status: this.status, + message: this.message, + header: this.header, + }; } /** diff --git a/src/types.ts b/src/types.ts index 5646d8c69..0d4bb5d09 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,11 @@ export type CustomError = Error & { - headers?: object; + headers?: Record; status?: number; statusCode?: number; code?: string; expose?: boolean; }; -export type ProtoImplClass = new(...args: any[]) => T; - export type AnyProto = { [key: string]: any; }; diff --git a/lib/application.test-d.ts b/test/application.test-d.ts similarity index 72% rename from lib/application.test-d.ts rename to test/application.test-d.ts index 3e16d1514..0bf037437 100644 --- a/lib/application.test-d.ts +++ b/test/application.test-d.ts @@ -1,6 +1,6 @@ import { expectType } from 'tsd'; -import { Context } from './application'; -import Application from './application'; +import { Context } from '../src/index.js'; +import Application from '../src/application.js'; const ctx = {} as Context; expectType(ctx.ip); diff --git a/test/application/context.test.ts b/test/application/context.test.ts index d9fca3623..a95a46d65 100644 --- a/test/application/context.test.ts +++ b/test/application/context.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app.context', () => { const app1 = new Koa(); diff --git a/test/application/currentContext.test.ts b/test/application/currentContext.test.ts index 6a1701c41..18d4aacc2 100644 --- a/test/application/currentContext.test.ts +++ b/test/application/currentContext.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app.currentContext', () => { it('should get currentContext', async () => { diff --git a/test/application/index.test.ts b/test/application/index.test.ts index d38c2ee38..f9706bd97 100644 --- a/test/application/index.test.ts +++ b/test/application/index.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert'; import request from 'supertest'; import CreateError from 'http-errors'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app', () => { // ignore test on Node.js v18 @@ -84,7 +84,9 @@ describe('app', () => { it('should have a static property exporting `HttpError` from http-errors library', () => { assert.notEqual(Koa.HttpError, undefined); assert.deepStrictEqual(Koa.HttpError, CreateError.HttpError); - assert.throws(() => { throw new CreateError(500, 'test error'); }, Koa.HttpError); + assert.throws(() => { + throw CreateError(500, 'test error'); + }, Koa.HttpError); }); it('should export createAsyncCtxStorageMiddleware function', () => { diff --git a/test/application/inspect.test.ts b/test/application/inspect.test.ts index 8f7fd122d..5c56e7aa1 100644 --- a/test/application/inspect.test.ts +++ b/test/application/inspect.test.ts @@ -1,10 +1,11 @@ import assert from 'node:assert'; import util from 'node:util'; -import Koa from '../..'; -const app = new Koa(); +import Koa from '../../src/index.js'; describe('app.inspect()', () => { + const app = new Koa(); + it('should work', () => { const str = util.inspect(app); assert.strictEqual("{ subdomainOffset: 2, proxy: false, env: 'test' }", str); diff --git a/test/application/onerror.test.ts b/test/application/onerror.test.ts index c9910d792..d2f6bf23f 100644 --- a/test/application/onerror.test.ts +++ b/test/application/onerror.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert'; import { runInNewContext } from 'node:vm'; import mm from 'mm'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app.onerror(err)', () => { afterEach(mm.restore); diff --git a/test/application/request.test.ts b/test/application/request.test.ts index f998bb823..3421af464 100644 --- a/test/application/request.test.ts +++ b/test/application/request.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app.request', () => { const app1 = new Koa(); diff --git a/test/application/respond.test.ts b/test/application/respond.test.ts index 016cb6e59..e8413968f 100644 --- a/test/application/respond.test.ts +++ b/test/application/respond.test.ts @@ -1,9 +1,10 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ import assert from 'node:assert'; import fs from 'node:fs'; import request from 'supertest'; import statuses from 'statuses'; -import Koa from '../..'; +import Koa from '../../src/index.js'; + +const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8')); describe('app.respond', () => { describe('when ctx.respond === false', () => { @@ -372,7 +373,7 @@ describe('app.respond', () => { describe('with custom status=700', () => { it('should respond with the associated status message', async () => { const app = new Koa(); - statuses['700'] = 'custom status'; + statuses.message['700'] = 'custom status'; app.use(ctx => { ctx.status = 700; @@ -385,7 +386,9 @@ describe('app.respond', () => { .expect(700) .expect('custom status'); - assert.strictEqual(res.res.statusMessage, 'custom status'); + assert.equal(res.statusCode, 700); + assert((res as any).res); + assert.strictEqual((res as any).res.statusMessage, 'custom status'); }); }); @@ -405,7 +408,9 @@ describe('app.respond', () => { .expect(200) .expect('ok'); - assert.strictEqual(res.res.statusMessage, 'ok'); + assert.equal(res.statusCode, 200); + assert((res as any).res); + assert.strictEqual((res as any).res.statusMessage, 'ok'); }); }); @@ -548,7 +553,6 @@ describe('app.respond', () => { .get('/') .expect('Content-Type', 'application/json; charset=utf-8'); - const pkg = require('../../package'); assert.strictEqual(res.headers.hasOwnProperty('content-length'), false); assert.deepStrictEqual(res.body, pkg); }); @@ -568,7 +572,6 @@ describe('app.respond', () => { .get('/') .expect('Content-Type', 'application/json; charset=utf-8'); - const pkg = require('../../package'); assert.strictEqual(res.headers.hasOwnProperty('content-length'), false); assert.deepStrictEqual(res.body, pkg); }); @@ -588,7 +591,6 @@ describe('app.respond', () => { .get('/') .expect('Content-Type', 'application/json; charset=utf-8'); - const pkg = require('../../package'); assert.strictEqual(res.headers.hasOwnProperty('content-length'), true); assert.deepStrictEqual(res.body, pkg); }); @@ -611,8 +613,8 @@ describe('app.respond', () => { .get('/') .expect('Content-Type', 'application/json; charset=utf-8'); - const pkg = require('../../package'); assert.strictEqual(res.headers.hasOwnProperty('content-length'), true); + assert.strictEqual(res.headers['content-length'], `${fs.readFileSync('package.json').length}`); assert.deepStrictEqual(res.body, pkg); }); diff --git a/test/application/response.test.ts b/test/application/response.test.ts index e32761ffe..80ea5de1b 100644 --- a/test/application/response.test.ts +++ b/test/application/response.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app.response', () => { const app1 = new Koa(); diff --git a/test/application/toJSON.test.ts b/test/application/toJSON.test.ts index 30bae0ce5..c9b70c8e0 100644 --- a/test/application/toJSON.test.ts +++ b/test/application/toJSON.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('app.toJSON()', () => { it('should work', () => { diff --git a/test/application/use.test.ts b/test/application/use.test.ts index c7427e1ec..c14fe3b63 100644 --- a/test/application/use.test.ts +++ b/test/application/use.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../../index'; +import Koa from '../../src/index.js'; describe('app.use(fn)', () => { it('should compose middleware', async () => { @@ -86,7 +86,7 @@ describe('app.use(fn)', () => { app.use((_ctx, next) => next()); assert.throws(() => { - app.use((function* generatorMiddileware(next) { + app.use((function* generatorMiddileware(_ctx: any, next: any) { console.log('pre generator'); yield next; // this.body = 'generator'; diff --git a/test/context/assert.test.ts b/test/context/assert.test.ts index 99428540b..7306a5c29 100644 --- a/test/context/assert.test.ts +++ b/test/context/assert.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.assert(value, status)', () => { it('should throw an error', () => { diff --git a/test/context/cookies.test.ts b/test/context/cookies.test.ts index fb4d35341..c2020b865 100644 --- a/test/context/cookies.test.ts +++ b/test/context/cookies.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('ctx.cookies', () => { describe('ctx.cookies.set()', () => { @@ -18,7 +18,11 @@ describe('ctx.cookies', () => { .get('/') .expect(204); - const cookie = res.headers['set-cookie'].some(cookie => /^name=/.test(cookie)); + let cookies = res.headers['set-cookie']; + if (Array.isArray(cookies)) { + cookies = cookies.join(','); + } + const cookie = /^name=/.test(cookies); assert.strictEqual(cookie, true); }); @@ -57,10 +61,12 @@ describe('ctx.cookies', () => { .get('/') .expect(204); - const cookies = res.headers['set-cookie']; - - assert.strictEqual(cookies.some(cookie => /^name=/.test(cookie)), true); - assert.strictEqual(cookies.some(cookie => /(,|^)name\.sig=/.test(cookie)), true); + let cookies = res.headers['set-cookie']; + if (Array.isArray(cookies)) { + cookies = cookies.join(','); + } + assert.strictEqual(/^name=/.test(cookies), true); + assert.strictEqual(/(,|^)name\.sig=/.test(cookies), true); }); }); @@ -83,10 +89,13 @@ describe('ctx.cookies', () => { .set('x-forwarded-proto', 'https') // mock secure .expect(204); - const cookies = res.headers['set-cookie']; - assert.strictEqual(cookies.some(cookie => /^name=/.test(cookie)), true); - assert.strictEqual(cookies.some(cookie => /(,|^)name\.sig=/.test(cookie)), true); - assert.strictEqual(cookies.every(cookie => /secure/.test(cookie)), true); + let cookies = res.headers['set-cookie']; + if (Array.isArray(cookies)) { + cookies = cookies.join(','); + } + assert.strictEqual(/^name=/.test(cookies), true); + assert.strictEqual(/(,|^)name\.sig=/.test(cookies), true); + assert.strictEqual(/secure/.test(cookies), true); }); }); }); @@ -97,7 +106,7 @@ describe('ctx.cookies', () => { app.use((ctx: any) => { ctx.cookies = { - set(key, value) { + set(key: string, value: string) { ctx.set(key, value); }, }; diff --git a/test/context/inspect.test.ts b/test/context/inspect.test.ts index e4085f7bb..fdfc53e5c 100644 --- a/test/context/inspect.test.ts +++ b/test/context/inspect.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import util from 'node:util'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.inspect()', () => { it('should return a json representation', () => { diff --git a/test/context/onerror.test.ts b/test/context/onerror.test.ts index c064b6e3d..aaa0caf7a 100644 --- a/test/context/onerror.test.ts +++ b/test/context/onerror.test.ts @@ -1,8 +1,8 @@ import assert from 'node:assert'; import request from 'supertest'; import { runInNewContext } from 'node:vm'; -import Koa from '../..'; -import context from '../test-helpers/context'; +import Koa from '../../src/index.js'; +import context from '../test-helpers/context.js'; describe('ctx.onerror(err)', () => { it('should respond', () => { diff --git a/test/context/state.test.ts b/test/context/state.test.ts index 535594188..455dddfbf 100644 --- a/test/context/state.test.ts +++ b/test/context/state.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('ctx.state', () => { it('should provide a ctx.state namespace', () => { diff --git a/test/context/throw.test.ts b/test/context/throw.test.ts index d9364a9ea..3ed3b8309 100644 --- a/test/context/throw.test.ts +++ b/test/context/throw.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.throw(msg)', () => { it('should set .status to 500', () => { @@ -7,7 +7,7 @@ describe('ctx.throw(msg)', () => { try { ctx.throw('boom'); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.status, 500); assert.strictEqual(err.expose, false); } @@ -21,7 +21,7 @@ describe('ctx.throw(err)', () => { try { ctx.throw(err); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.status, 500); assert.strictEqual(err.message, 'test'); assert.strictEqual(err.expose, false); @@ -36,7 +36,7 @@ describe('ctx.throw(err, status)', () => { try { ctx.throw(error, 422); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.status, 422); assert.strictEqual(err.message, 'test'); assert.strictEqual(err.expose, true); @@ -51,7 +51,7 @@ describe('ctx.throw(status, err)', () => { try { ctx.throw(422, error); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.status, 422); assert.strictEqual(err.message, 'test'); assert.strictEqual(err.expose, true); @@ -65,7 +65,7 @@ describe('ctx.throw(msg, status)', () => { try { ctx.throw('name required', 400); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'name required'); assert.strictEqual(err.status, 400); assert.strictEqual(err.expose, true); @@ -79,7 +79,7 @@ describe('ctx.throw(status, msg)', () => { try { ctx.throw(400, 'name required'); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'name required'); assert.strictEqual(400, err.status); assert.strictEqual(true, err.expose); @@ -87,19 +87,46 @@ describe('ctx.throw(status, msg)', () => { }); }); +describe('ctx.throw(status, errorProps)', () => { + it('should throw an error', () => { + const ctx = context(); + + try { + ctx.throw(400, { foo: 'bar' }); + } catch (err: any) { + assert.strictEqual(err.message, 'Bad Request'); + assert.strictEqual(400, err.status); + assert.strictEqual(true, err.expose); + assert.strictEqual('bar', err.foo); + } + }); +}); + describe('ctx.throw(status)', () => { it('should throw an error', () => { const ctx = context(); try { ctx.throw(400); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'Bad Request'); assert.strictEqual(err.status, 400); assert.strictEqual(err.expose, true); } }); + it('should 500 status instead of invalid status', () => { + const ctx = context(); + + try { + ctx.throw(999); + } catch (err: any) { + assert.strictEqual(err.message, 'Internal Server Error'); + assert.strictEqual(err.status, 500); + assert.strictEqual(err.expose, false); + } + }); + describe('when not valid status', () => { it('should not expose', () => { const ctx = context(); @@ -108,9 +135,10 @@ describe('ctx.throw(status)', () => { const err = new Error('some error'); (err as any).status = -1; ctx.throw(err); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'some error'); assert.strictEqual(err.expose, false); + assert.strictEqual(err.status, 500); } }); }); @@ -122,7 +150,7 @@ describe('ctx.throw(status, msg, props)', () => { try { ctx.throw(400, 'msg', { prop: true }); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'msg'); assert.strictEqual(err.status, 400); assert.strictEqual(err.expose, true); @@ -139,7 +167,7 @@ describe('ctx.throw(status, msg, props)', () => { prop: true, status: -1, }); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'msg'); assert.strictEqual(err.status, 400); assert.strictEqual(err.expose, true); @@ -155,7 +183,7 @@ describe('ctx.throw(msg, props)', () => { try { ctx.throw('msg', { prop: true }); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'msg'); assert.strictEqual(err.status, 500); assert.strictEqual(err.expose, false); @@ -170,7 +198,7 @@ describe('ctx.throw(status, props)', () => { try { ctx.throw(400, { prop: true }); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'Bad Request'); assert.strictEqual(err.status, 400); assert.strictEqual(err.expose, true); @@ -185,7 +213,7 @@ describe('ctx.throw(err, props)', () => { try { ctx.throw(new Error('test'), { prop: true }); - } catch (err) { + } catch (err: any) { assert.strictEqual(err.message, 'test'); assert.strictEqual(err.status, 500); assert.strictEqual(err.expose, false); diff --git a/test/context/toJSON.test.ts b/test/context/toJSON.test.ts index 3faaf08e7..8cb7b9b2f 100644 --- a/test/context/toJSON.test.ts +++ b/test/context/toJSON.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.toJSON()', () => { it('should return a json representation', () => { diff --git a/test/request/accept.test.ts b/test/request/accept.test.ts index 693357227..270a878c3 100644 --- a/test/request/accept.test.ts +++ b/test/request/accept.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import Accept from 'accepts'; -import context, { request as Request } from '../test-helpers/context'; +import context, { request as Request } from '../test-helpers/context.js'; describe('ctx.accept', () => { it('should return an Accept instance', () => { diff --git a/test/request/accepts.test.ts b/test/request/accepts.test.ts index 79931f1b1..21ce6a873 100644 --- a/test/request/accepts.test.ts +++ b/test/request/accepts.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.accepts(types)', () => { describe('with no arguments', () => { diff --git a/test/request/acceptsCharsets.test.ts b/test/request/acceptsCharsets.test.ts index 6f1b6c9c3..c46922315 100644 --- a/test/request/acceptsCharsets.test.ts +++ b/test/request/acceptsCharsets.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.acceptsCharsets()', () => { describe('with no arguments', () => { diff --git a/test/request/acceptsEncodings.test.ts b/test/request/acceptsEncodings.test.ts index c37ced0cd..57e39907f 100644 --- a/test/request/acceptsEncodings.test.ts +++ b/test/request/acceptsEncodings.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.acceptsEncodings()', () => { describe('with no arguments', () => { diff --git a/test/request/acceptsLanguages.test.ts b/test/request/acceptsLanguages.test.ts index a43b0dda6..e6975b510 100644 --- a/test/request/acceptsLanguages.test.ts +++ b/test/request/acceptsLanguages.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.acceptsLanguages(langs)', () => { describe('with no arguments', () => { diff --git a/test/request/charset.test.ts b/test/request/charset.test.ts index 33be4270d..e1aa6d8ff 100644 --- a/test/request/charset.test.ts +++ b/test/request/charset.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.charset', () => { describe('with no content-type present', () => { diff --git a/test/request/fresh.test.ts b/test/request/fresh.test.ts index b39127e72..5d5af6487 100644 --- a/test/request/fresh.test.ts +++ b/test/request/fresh.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.fresh', () => { describe('the request method is not GET and HEAD', () => { diff --git a/test/request/get.test.ts b/test/request/get.test.ts index 23f37cfcf..6030cd4e0 100644 --- a/test/request/get.test.ts +++ b/test/request/get.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.get(name)', () => { it('should return the field value', () => { diff --git a/test/request/header.test.ts b/test/request/header.test.ts index b140d436f..0023de2d7 100644 --- a/test/request/header.test.ts +++ b/test/request/header.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.header', () => { it('should return the request header object', () => { diff --git a/test/request/headers.test.ts b/test/request/headers.test.ts index 501edefb3..4caa24797 100644 --- a/test/request/headers.test.ts +++ b/test/request/headers.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.headers', () => { it('should return the request header object', () => { diff --git a/test/request/host.test.ts b/test/request/host.test.ts index 8150aaa6b..ed01b16e0 100644 --- a/test/request/host.test.ts +++ b/test/request/host.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.host', () => { it('should return host with port', () => { diff --git a/test/request/hostname.test.ts b/test/request/hostname.test.ts index d7cff1226..6ed4f3e83 100644 --- a/test/request/hostname.test.ts +++ b/test/request/hostname.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.hostname', () => { it('should return hostname void of port', () => { diff --git a/test/request/href.test.ts b/test/request/href.test.ts index 262e65b3a..174642bdf 100644 --- a/test/request/href.test.ts +++ b/test/request/href.test.ts @@ -2,8 +2,8 @@ import assert from 'node:assert'; import Stream from 'node:stream'; import http from 'node:http'; import type { AddressInfo } from 'node:net'; -import Koa from '../../'; -import context from '../test-helpers/context'; +import Koa from '../../src/index.js'; +import context from '../test-helpers/context.js'; describe('ctx.href', () => { it('should return the full request url', () => { diff --git a/test/request/idempotent.test.ts b/test/request/idempotent.test.ts index 386d845c7..1e582b4df 100644 --- a/test/request/idempotent.test.ts +++ b/test/request/idempotent.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('ctx.idempotent', () => { describe('when the request method is idempotent', () => { diff --git a/test/request/inspect.test.ts b/test/request/inspect.test.ts index 5f5f787ca..48bcdf6b3 100644 --- a/test/request/inspect.test.ts +++ b/test/request/inspect.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import util from 'node:util'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('req.inspect()', () => { describe('with no request.req present', () => { diff --git a/test/request/ip.test.ts b/test/request/ip.test.ts index fd2ecd0ce..923850645 100644 --- a/test/request/ip.test.ts +++ b/test/request/ip.test.ts @@ -1,13 +1,13 @@ import assert from 'node:assert'; import Stream from 'node:stream'; -import Koa from '../..'; -import { request as Request } from '../test-helpers/context'; +import Koa from '../../src/index.js'; +import { request as Request } from '../test-helpers/context.js'; describe('req.ip', () => { describe('with req.ips present', () => { it('should return req.ips[0]', () => { const app = new Koa(); - const req = { headers: {}, socket: new Stream.Duplex() }; + const req = { headers: {} as Record, socket: new Stream.Duplex() }; app.proxy = true; req.headers['x-forwarded-for'] = '127.0.0.1'; (req.socket as any).remoteAddress = '127.0.0.2'; diff --git a/test/request/ips.test.ts b/test/request/ips.test.ts index 0934c0e52..e79e88d1c 100644 --- a/test/request/ips.test.ts +++ b/test/request/ips.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.ips', () => { describe('when X-Forwarded-For is present', () => { diff --git a/test/request/is.test.ts b/test/request/is.test.ts index 1188a7807..78dd04359 100644 --- a/test/request/is.test.ts +++ b/test/request/is.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.is(type)', () => { it('should ignore params', () => { diff --git a/test/request/length.test.ts b/test/request/length.test.ts index bc0811cb7..e377a523c 100644 --- a/test/request/length.test.ts +++ b/test/request/length.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('ctx.length', () => { it('should return length in content-length', () => { diff --git a/test/request/origin.test.ts b/test/request/origin.test.ts index 782f1be36..9a46f22b3 100644 --- a/test/request/origin.test.ts +++ b/test/request/origin.test.ts @@ -1,6 +1,6 @@ import Stream from 'node:stream'; import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.origin', () => { it('should return the origin of url', () => { diff --git a/test/request/path.test.ts b/test/request/path.test.ts index 0224ca056..84e745b57 100644 --- a/test/request/path.test.ts +++ b/test/request/path.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import parseurl from 'parseurl'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.path', () => { it('should return the pathname', () => { @@ -32,6 +32,6 @@ describe('ctx.path=', () => { const ctx = context({ url: '/login?foo=bar' }); ctx.path = '/login'; const url = parseurl(ctx.req); - assert.strictEqual(url.path, '/login?foo=bar'); + assert.strictEqual(url?.path, '/login?foo=bar'); }); }); diff --git a/test/request/protocol.test.ts b/test/request/protocol.test.ts index 8b3c2a01e..a941a7e9a 100644 --- a/test/request/protocol.test.ts +++ b/test/request/protocol.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.protocol', () => { describe('when encrypted', () => { diff --git a/test/request/query.test.ts b/test/request/query.test.ts index 7b94bf62f..e10b2f0a4 100644 --- a/test/request/query.test.ts +++ b/test/request/query.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.query', () => { describe('when missing', () => { diff --git a/test/request/querystring.test.ts b/test/request/querystring.test.ts index ecba75afc..207c20c1b 100644 --- a/test/request/querystring.test.ts +++ b/test/request/querystring.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert'; import parseurl from 'parseurl'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.querystring', () => { it('should return the querystring', () => { @@ -46,6 +46,6 @@ describe('ctx.querystring=', () => { const ctx = context({ url: '/login?foo=bar' }); ctx.querystring = 'foo=bar'; const url = parseurl(ctx.req); - assert.strictEqual(url.path, '/login?foo=bar'); + assert.strictEqual(url?.path, '/login?foo=bar'); }); }); diff --git a/test/request/search.test.ts b/test/request/search.test.ts index c55f5f728..493faafab 100644 --- a/test/request/search.test.ts +++ b/test/request/search.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.search=', () => { it('should replace the search', () => { diff --git a/test/request/secure.test.ts b/test/request/secure.test.ts index bcbb1b98c..e8eb879de 100644 --- a/test/request/secure.test.ts +++ b/test/request/secure.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.secure', () => { it('should return true when encrypted', () => { diff --git a/test/request/stale.test.ts b/test/request/stale.test.ts index c79f43c9b..d2d5ca94b 100644 --- a/test/request/stale.test.ts +++ b/test/request/stale.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('req.stale', () => { it('should be the inverse of req.fresh', () => { diff --git a/test/request/subdomains.test.ts b/test/request/subdomains.test.ts index 6bb232961..43d222562 100644 --- a/test/request/subdomains.test.ts +++ b/test/request/subdomains.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.subdomains', () => { it('should return subdomain array', () => { diff --git a/test/request/type.test.ts b/test/request/type.test.ts index ae32382c8..408c3ca90 100644 --- a/test/request/type.test.ts +++ b/test/request/type.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.type', () => { it('should return type void of parameters', () => { diff --git a/test/request/whatwg-url.test.ts b/test/request/whatwg-url.test.ts index e413056ca..1092f9690 100644 --- a/test/request/whatwg-url.test.ts +++ b/test/request/whatwg-url.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { request } from '../test-helpers/context'; +import { request } from '../test-helpers/context.js'; describe('req.URL', () => { it('should not throw when host is void', () => { diff --git a/test/response/append.test.ts b/test/response/append.test.ts index 791c2bdeb..97f6dba9e 100644 --- a/test/response/append.test.ts +++ b/test/response/append.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.append(name, val)', () => { it('should append multiple headers', () => { diff --git a/test/response/attachment.test.ts b/test/response/attachment.test.ts index c2276b295..1edccfca0 100644 --- a/test/response/attachment.test.ts +++ b/test/response/attachment.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert'; import request from 'supertest'; -import context from '../test-helpers/context'; -import Koa from '../..'; +import context from '../test-helpers/context.js'; +import Koa from '../../src/index.js'; describe('ctx.attachment([filename])', () => { describe('when given a filename', () => { diff --git a/test/response/body.test.ts b/test/response/body.test.ts index 93acec1b0..71d7b38d5 100644 --- a/test/response/body.test.ts +++ b/test/response/body.test.ts @@ -1,7 +1,7 @@ import fs from 'node:fs'; import Stream from 'node:stream'; import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.body=', () => { describe('when Content-Type is set', () => { diff --git a/test/response/etag.test.ts b/test/response/etag.test.ts index dc1bd4364..c839c060c 100644 --- a/test/response/etag.test.ts +++ b/test/response/etag.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.etag=', () => { it('should not modify an etag with quotes', () => { diff --git a/test/response/flushHeaders.test.ts b/test/response/flushHeaders.test.ts index 15a8ddaab..17360d483 100644 --- a/test/response/flushHeaders.test.ts +++ b/test/response/flushHeaders.test.ts @@ -3,7 +3,7 @@ import http from 'node:http'; import { PassThrough } from 'node:stream'; import type { AddressInfo } from 'node:net'; import request from 'supertest'; -import Koa from '../..'; +import Koa from '../../src/index.js'; describe('ctx.flushHeaders()', () => { it('should set headersSent', () => { @@ -98,7 +98,7 @@ describe('ctx.flushHeaders()', () => { }, 10000); }); - const server = app.listen(function(err) { + const server = app.listen(function(err: Error) { if (err) return done(err); const port = (server.address() as AddressInfo).port; diff --git a/test/response/has.test.ts b/test/response/has.test.ts index 2f069aadd..6fa470c8f 100644 --- a/test/response/has.test.ts +++ b/test/response/has.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.response.has(name)', () => { it('should check a field value, case insensitive way', () => { diff --git a/test/response/header.test.ts b/test/response/header.test.ts index 522c3181b..332e68d03 100644 --- a/test/response/header.test.ts +++ b/test/response/header.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert'; import request from 'supertest'; -import { response } from '../test-helpers/context'; -import Koa from '../..'; +import { response } from '../test-helpers/context.js'; +import Koa from '../../src/index.js'; describe('res.header', () => { it('should return the response header object', () => { diff --git a/test/response/headers.test.ts b/test/response/headers.test.ts index a10867ce5..ff79fd907 100644 --- a/test/response/headers.test.ts +++ b/test/response/headers.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.header', () => { it('should return the response header object', () => { diff --git a/test/response/inspect.test.ts b/test/response/inspect.test.ts index a212cd31d..9de860872 100644 --- a/test/response/inspect.test.ts +++ b/test/response/inspect.test.ts @@ -1,6 +1,6 @@ import util from 'node:util'; import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.inspect()', () => { describe('with no response.res present', () => { diff --git a/test/response/is.test.ts b/test/response/is.test.ts index 786eabafa..c5e788269 100644 --- a/test/response/is.test.ts +++ b/test/response/is.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('response.is(type)', () => { it('should ignore params', () => { diff --git a/test/response/last-modified.test.ts b/test/response/last-modified.test.ts index 6632b147a..ceaafbe21 100644 --- a/test/response/last-modified.test.ts +++ b/test/response/last-modified.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.lastModified', () => { it('should set the header as a UTCString', () => { diff --git a/test/response/length.test.ts b/test/response/length.test.ts index 5e3fe89e4..e056a5cda 100644 --- a/test/response/length.test.ts +++ b/test/response/length.test.ts @@ -1,6 +1,6 @@ import fs from 'node:fs'; import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.length', () => { describe('when Content-Length is defined', () => { diff --git a/test/response/message.test.ts b/test/response/message.test.ts index 1ea7ced79..6bb3743ae 100644 --- a/test/response/message.test.ts +++ b/test/response/message.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.message', () => { it('should return the response status message', () => { @@ -23,6 +23,6 @@ describe('res.message=', () => { res.status = 200; res.message = 'ok'; assert.strictEqual(res.res.statusMessage, 'ok'); - assert.strictEqual(res.inspect().message, 'ok'); + assert.strictEqual(res.inspect()!.message, 'ok'); }); }); diff --git a/test/response/redirect.test.ts b/test/response/redirect.test.ts index 6b69f23dd..7a6a250f3 100644 --- a/test/response/redirect.test.ts +++ b/test/response/redirect.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert'; import request from 'supertest'; -import context from '../test-helpers/context'; -import Koa from '../..'; +import context from '../test-helpers/context.js'; +import Koa from '../../src/index.js'; describe('ctx.redirect(url)', () => { it('should redirect to the given url', () => { @@ -132,7 +132,7 @@ describe('ctx.redirect(url)', () => { }); }); -function escape(html) { +function escape(html: string) { return String(html) .replace(/&/g, '&') .replace(/"/g, '"') diff --git a/test/response/remove.test.ts b/test/response/remove.test.ts index de65a875e..9fd20fd11 100644 --- a/test/response/remove.test.ts +++ b/test/response/remove.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.remove(name)', () => { it('should remove a field', () => { diff --git a/test/response/set.test.ts b/test/response/set.test.ts index aa5042197..fda033270 100644 --- a/test/response/set.test.ts +++ b/test/response/set.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.set(name, val)', () => { it('should set a field value', () => { diff --git a/test/response/socket.test.ts b/test/response/socket.test.ts index acef45281..85ae4bbac 100644 --- a/test/response/socket.test.ts +++ b/test/response/socket.test.ts @@ -1,6 +1,6 @@ import Stream from 'node:stream'; import assert from 'node:assert'; -import { response } from '../test-helpers/context'; +import { response } from '../test-helpers/context.js'; describe('res.socket', () => { it('should return the request socket object', () => { diff --git a/test/response/status.test.ts b/test/response/status.test.ts index 9b9c26b37..29a0decdd 100644 --- a/test/response/status.test.ts +++ b/test/response/status.test.ts @@ -1,8 +1,8 @@ import assert from 'node:assert'; import request from 'supertest'; import statuses from 'statuses'; -import { response } from '../test-helpers/context'; -import Koa from '../..'; +import { response } from '../test-helpers/context.js'; +import Koa from '../../src/index.js'; describe('res.status=', () => { describe('when a status code', () => { @@ -28,7 +28,7 @@ describe('res.status=', () => { describe('and custom status', () => { beforeEach(() => { - statuses['700'] = 'custom status'; + statuses.message['700'] = 'custom status'; }); it('should set the status', () => { @@ -62,7 +62,7 @@ describe('res.status=', () => { }); }); - function strip(status) { + function strip(status: number) { it('should strip content related header fields', async () => { const app = new Koa(); diff --git a/test/response/type.test.ts b/test/response/type.test.ts index 3b45f7e43..789c35a17 100644 --- a/test/response/type.test.ts +++ b/test/response/type.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.type=', () => { describe('with a mime', () => { diff --git a/test/response/vary.test.ts b/test/response/vary.test.ts index d4941649f..af5b2cd1c 100644 --- a/test/response/vary.test.ts +++ b/test/response/vary.test.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; -import context from '../test-helpers/context'; +import context from '../test-helpers/context.js'; describe('ctx.vary(field)', () => { describe('when Vary is not set', () => { diff --git a/test/response/writable.test.ts b/test/response/writable.test.ts index 92965247d..9ec50f7fe 100644 --- a/test/response/writable.test.ts +++ b/test/response/writable.test.ts @@ -1,10 +1,11 @@ import net from 'node:net'; import assert from 'node:assert'; -import Koa from '../../'; +import { setTimeout as sleep } from 'node:timers/promises'; +import Koa from '../../src/index.js'; describe('res.writable', () => { describe('when continuous requests in one persistent connection', () => { - function requestTwice(server, done) { + function requestTwice(server: any, done: any) { const port = server.address().port; const buf = Buffer.from('GET / HTTP/1.1\r\nHost: localhost:' + port + '\r\nConnection: keep-alive\r\n\r\n'); const client = net.connect(port); @@ -27,7 +28,7 @@ describe('res.writable', () => { }); const server = app.listen(); - requestTwice(server, (_, datas) => { + requestTwice(server, (_: any, datas: Buffer[]) => { const responses = Buffer.concat(datas).toString(); assert.strictEqual(/request 1, writable: true/.test(responses), true); assert.strictEqual(/request 2, writable: true/.test(responses), true); @@ -37,7 +38,7 @@ describe('res.writable', () => { }); describe('when socket closed before response sent', () => { - function requestClosed(server) { + function requestClosed(server: any) { const port = server.address().port; const buf = Buffer.from('GET / HTTP/1.1\r\nHost: localhost:' + port + '\r\nConnection: keep-alive\r\n\r\n'); const client = net.connect(port); @@ -62,7 +63,7 @@ describe('res.writable', () => { }); describe('when response finished', () => { - function request(server) { + function request(server: any) { const port = server.address().port; const buf = Buffer.from('GET / HTTP/1.1\r\nHost: localhost:' + port + '\r\nConnection: keep-alive\r\n\r\n'); const client = net.connect(port); @@ -86,7 +87,3 @@ describe('res.writable', () => { }); }); }); - -function sleep(time) { - return new Promise(resolve => setTimeout(resolve, time)); -} diff --git a/test/test-helpers/context.ts b/test/test-helpers/context.ts index f2e204ae4..570c75e55 100644 --- a/test/test-helpers/context.ts +++ b/test/test-helpers/context.ts @@ -1,6 +1,6 @@ import stream from 'node:stream'; -import Koa from '../../src/application'; -import type { ContextDelegation } from '../../src/context'; +import Koa from '../../src/application.js'; +import type { ContextDelegation } from '../../src/context.js'; export default function context(req?: any, res?: any, app?: Koa) { const socket = new stream.Duplex(); diff --git a/tsconfig.json b/tsconfig.json index 1e5143c6a..ff41b7342 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,10 @@ { "extends": "@eggjs/tsconfig", "compilerOptions": { + "strict": true, + "noImplicitAny": true, "target": "ES2022", - "module": "Node16", - "outDir": "lib", - "useUnknownInCatchVariables": false - }, - "include": [ - "src" - ], - "exclude": [ - "node_modules", - "test" - ] + "module": "NodeNext", + "moduleResolution": "NodeNext" + } } From 0313d6d4e3bf4bd884598d2bce04459e0e74d5d4 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sat, 8 Jun 2024 23:33:53 +0800 Subject: [PATCH 2/2] f --- src/application.ts | 10 +--------- test/application/index.test.ts | 5 ----- test/context/assert.test.ts | 10 ++++++++++ test/response/length.test.ts | 2 ++ 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/application.ts b/src/application.ts index 166ecb8fe..3ba36d9b8 100644 --- a/src/application.ts +++ b/src/application.ts @@ -166,7 +166,7 @@ export default class Application extends Emitter { * return current context from async local storage */ get currentContext() { - if (this.ctxStorage) return this.ctxStorage.getStore(); + return this.ctxStorage.getStore(); } /** @@ -214,14 +214,6 @@ export default class Application extends Emitter { console.error(`\n${msg.replace(/^/gm, ' ')}\n`); } - createAsyncCtxStorageMiddleware() { - return async (ctx: ContextDelegation, next: Next) => { - await this.ctxStorage.run(ctx, async () => { - return await next(); - }); - }; - } - /** * Response helper. */ diff --git a/test/application/index.test.ts b/test/application/index.test.ts index f9706bd97..56764c8e4 100644 --- a/test/application/index.test.ts +++ b/test/application/index.test.ts @@ -88,9 +88,4 @@ describe('app', () => { throw CreateError(500, 'test error'); }, Koa.HttpError); }); - - it('should export createAsyncCtxStorageMiddleware function', () => { - const app = new Koa(); - assert.strictEqual(typeof app.createAsyncCtxStorageMiddleware, 'function'); - }); }); diff --git a/test/context/assert.test.ts b/test/context/assert.test.ts index 7306a5c29..bcdab6b05 100644 --- a/test/context/assert.test.ts +++ b/test/context/assert.test.ts @@ -9,8 +9,18 @@ describe('ctx.assert(value, status)', () => { ctx.assert(false, 404); throw new Error('asdf'); } catch (err: any) { + assert.strictEqual(err.message, 'Not Found'); assert.strictEqual(err.status, 404); assert.strictEqual(err.expose, true); } + + try { + ctx.assert(false, 401, 'Please login!'); + throw new Error('asdf'); + } catch (err: any) { + assert.strictEqual(err.message, 'Please login!'); + assert.strictEqual(err.status, 401); + assert.strictEqual(err.expose, true); + } }); }); diff --git a/test/response/length.test.ts b/test/response/length.test.ts index e056a5cda..c9232d788 100644 --- a/test/response/length.test.ts +++ b/test/response/length.test.ts @@ -67,6 +67,8 @@ describe('res.length=', () => { const res = response(); res.length = 100; assert.strictEqual(res.length, 100); + res.length = undefined; + assert.strictEqual(res.length, 100); }); it('should not set when Transfer-Encoding present', () => {