diff --git a/.eslintrc b/.eslintrc index c799fe5..9bcdb46 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,6 @@ { - "extends": "eslint-config-egg" + "extends": [ + "eslint-config-egg/typescript", + "eslint-config-egg/lib/rules/enforce-node-prefix" + ] } diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 774e7ea..d876c2e 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -12,6 +12,6 @@ jobs: uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: os: 'ubuntu-latest' - version: '14, 16, 18, 20, 22' + version: '18.19.0, 18, 20, 22' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 04ee2ea..01112e2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,5 @@ dump.rdb .DS_Store test.js +.tshy* +dist diff --git a/README.md b/README.md index 3eebd84..b97b9e3 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,9 @@ npm install egg-path-matching ## Usage -```js -const pathMatching = require('egg-path-matching'); +```ts +import { pathMatching } from 'egg-path-matching'; + const options = { ignore: '/api', // string will use parsed by path-to-regexp // support regexp @@ -36,17 +37,18 @@ const options = { }; const match = pathMatching(options); -assert(match({ path: '/api' }) === true); -assert(match({ path: '/api/hello' }) === true); -assert(match({ path: '/api' }) === true); +assert.equal(match({ path: '/api' }), true); +assert.equal(match({ path: '/api/hello' }), true); +assert.equal(match({ path: '/api' }), true); ``` ### options -- `match` {String | RegExp | Function | Array} - if request path hit `options.match`, will return true, otherwise will return false. -- `ignore` {String | RegExp | Function | Array} - if request path hit `options.ignore`, will return false, otherwise will return true. +- `match` {String | RegExp | Function | Array} - if request path hit `options.match`, will return `true`, otherwise will return `false`. +- `ignore` {String | RegExp | Function | Array} - if request path hit `options.ignore`, will return `false`, otherwise will return `true`. -`ignore` and `match` can not both be presented. and if neither `ignore` nor `match` presented, the new function will always return true. +`ignore` and `match` can not both be presented. +and if neither `ignore` nor `match` presented, the new function will always return `true`. ### License diff --git a/index.js b/index.js deleted file mode 100644 index 5b6c654..0000000 --- a/index.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -const pathToRegexp = require('path-to-regexp'); - -module.exports = function(options) { - options = options || {}; - if (options.match && options.ignore) throw new Error('options.match and options.ignore can not both present'); - if (!options.match && !options.ignore) return () => true; - - const matchFn = options.match ? toPathMatch(options.match) : toPathMatch(options.ignore); - - return function pathMatch(ctx) { - const matched = matchFn(ctx); - return options.match ? matched : !matched; - }; -}; - -function toPathMatch(pattern) { - if (typeof pattern === 'string') { - const reg = pathToRegexp(pattern, [], { end: false }); - if (reg.global) reg.lastIndex = 0; - return ctx => reg.test(ctx.path); - } - if (pattern instanceof RegExp) { - return ctx => { - if (pattern.global) pattern.lastIndex = 0; - return pattern.test(ctx.path); - }; - } - if (typeof pattern === 'function') return pattern; - if (Array.isArray(pattern)) { - const matchs = pattern.map(item => toPathMatch(item)); - return ctx => matchs.some(match => match(ctx)); - } - throw new Error('match/ignore pattern must be RegExp, Array or String, but got ' + pattern); -} diff --git a/package.json b/package.json index 8d48785..f2c704c 100644 --- a/package.json +++ b/package.json @@ -2,19 +2,16 @@ "name": "egg-path-matching", "version": "1.1.0", "engine": { - "node": ">= 4.0.0" + "node": ">= 18.19.0" }, "description": "match or ignore url path", - "main": "index.js", - "files": [ - "index.js" - ], "scripts": { - "test": "egg-bin test", - "cov": "egg-bin cov", - "lint": "eslint *.js test", - "ci": "npm run lint && npm run cov", - "contributor": "git-contributor" + "lint": "eslint src test", + "test": "npm run lint -- --fix && npm run test-local", + "test-local": "egg-bin test", + "ci": "npm run lint && egg-bin cov && npm run prepublishOnly", + "contributor": "git-contributor", + "prepublishOnly": "tshy && tshy-after" }, "keywords": [ "url", @@ -32,12 +29,46 @@ }, "license": "MIT", "dependencies": { - "path-to-regexp": "^1.7.0" + "path-to-regexp": "^6.2.2" }, "devDependencies": { - "egg-bin": "^6.5.2", - "eslint": "^8.55.0", - "eslint-config-egg": "12", - "git-contributor": "^2.1.5" - } + "@eggjs/tsconfig": "1", + "@types/mocha": "10", + "@types/node": "20", + "egg-bin": "6", + "eslint": "8", + "eslint-config-egg": "13", + "git-contributor": "2", + "tshy": "1", + "tshy-after": "1", + "typescript": "5" + }, + "files": [ + "dist", + "src" + ], + "type": "module", + "tshy": { + "exports": { + "./package.json": "./package.json", + ".": "./src/index.ts" + } + }, + "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" + } + } + }, + "main": "./dist/commonjs/index.js", + "types": "./dist/commonjs/index.d.ts" } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..3c875a1 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,49 @@ +import { pathToRegexp } from 'path-to-regexp'; + +export type PathMatchingFun = (ctx: any) => boolean; + +export type PathMatchingPattern = string | RegExp | PathMatchingFun | (string | RegExp | PathMatchingFun)[]; + +export interface PathMatchingOptions { + ignore?: PathMatchingPattern; + match?: PathMatchingPattern; +} + +export function pathMatching(options: PathMatchingOptions): PathMatchingFun { + options = options || {}; + if (options.match && options.ignore) { + throw new Error('options.match and options.ignore can not both present'); + } + if (!options.match && !options.ignore) { + return () => true; + } + + const matchFn = options.match ? toPathMatch(options.match) : toPathMatch(options.ignore!); + + return function pathMatch(ctx: any) { + const matched = matchFn(ctx); + return options.match ? matched : !matched; + }; +} + +function toPathMatch(pattern: PathMatchingPattern): PathMatchingFun { + if (typeof pattern === 'string') { + const reg = pathToRegexp(pattern, [], { end: false }); + if (reg.global) reg.lastIndex = 0; + return ctx => reg.test(ctx.path); + } + if (pattern instanceof RegExp) { + return ctx => { + if (pattern.global) { + pattern.lastIndex = 0; + } + return pattern.test(ctx.path); + }; + } + if (typeof pattern === 'function') return pattern; + if (Array.isArray(pattern)) { + const matchFns = pattern.map(item => toPathMatch(item)); + return ctx => matchFns.some(matchFn => matchFn(ctx)); + } + throw new Error(`match/ignore pattern must be RegExp, Array or String, but got ${pattern}`); +} diff --git a/test/index.test.js b/test/index.test.ts similarity index 94% rename from test/index.test.js rename to test/index.test.ts index 6d8cc36..457337f 100644 --- a/test/index.test.js +++ b/test/index.test.ts @@ -1,21 +1,19 @@ -'use strict'; - -const assert = require('assert'); -const match = require('..'); +import { strict as assert } from 'node:assert'; +import { pathMatching as match } from '../src/index.js'; describe('egg-path-matching', () => { it('options.match and options.ignore both present should throw', () => { try { match({ ignore: '/api', match: '/dashboard' }); throw new Error('should not exec'); - } catch (e) { - assert(e.message === 'options.match and options.ignore can not both present'); + } catch (e: any) { + assert.equal(e.message, 'options.match and options.ignore can not both present'); } }); it('options.match and options.ignore both not present should always return true', () => { const fn = match({}); - assert(fn() === true); + assert(fn({}) === true); }); describe('match', () => { diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ff41b73 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@eggjs/tsconfig", + "compilerOptions": { + "strict": true, + "noImplicitAny": true, + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext" + } +}