0
0
mirror of https://github.com/honojs/hono.git synced 2024-11-21 10:08:58 +01:00

refactor(router): use # for private props to reduce the minified file size (#3660)

This commit is contained in:
EdamAmex 2024-11-13 04:38:10 +09:00 committed by GitHub
parent 7e17b76ce9
commit 7b308358f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 93 additions and 90 deletions

View File

@ -10,7 +10,7 @@ const splitPathRe = /\/(:\w+(?:{(?:(?:{[\d,]+})|[^}])+})?)|\/[^\/\?]+|(\?)/g
const splitByStarRe = /\*/
export class LinearRouter<T> implements Router<T> {
name: string = 'LinearRouter'
routes: [string, string, T][] = []
#routes: [string, string, T][] = []
add(method: string, path: string, handler: T) {
for (
@ -18,14 +18,14 @@ export class LinearRouter<T> implements Router<T> {
i < len;
i++
) {
this.routes.push([method, paths[i], handler])
this.#routes.push([method, paths[i], handler])
}
}
match(method: string, path: string): Result<T> {
const handlers: [T, Params][] = []
ROUTES_LOOP: for (let i = 0, len = this.routes.length; i < len; i++) {
const [routeMethod, routePath, handler] = this.routes[i]
ROUTES_LOOP: for (let i = 0, len = this.#routes.length; i < len; i++) {
const [routeMethod, routePath, handler] = this.#routes[i]
if (routeMethod === method || routeMethod === METHOD_NAME_ALL) {
if (routePath === '*' || routePath === '/*') {
handlers.push([handler, emptyParams])

View File

@ -43,9 +43,9 @@ function compareKey(a: string, b: string): number {
}
export class Node {
index?: number
varIndex?: number
children: Record<string, Node> = Object.create(null)
#index?: number
#varIndex?: number
#children: Record<string, Node> = Object.create(null)
insert(
tokens: readonly string[],
@ -55,14 +55,14 @@ export class Node {
pathErrorCheckOnly: boolean
): void {
if (tokens.length === 0) {
if (this.index !== undefined) {
if (this.#index !== undefined) {
throw PATH_ERROR
}
if (pathErrorCheckOnly) {
return
}
this.index = index
this.#index = index
return
}
@ -88,10 +88,10 @@ export class Node {
}
}
node = this.children[regexpStr]
node = this.#children[regexpStr]
if (!node) {
if (
Object.keys(this.children).some(
Object.keys(this.#children).some(
(k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
)
) {
@ -100,19 +100,19 @@ export class Node {
if (pathErrorCheckOnly) {
return
}
node = this.children[regexpStr] = new Node()
node = this.#children[regexpStr] = new Node()
if (name !== '') {
node.varIndex = context.varIndex++
node.#varIndex = context.varIndex++
}
}
if (!pathErrorCheckOnly && name !== '') {
paramMap.push([name, node.varIndex as number])
paramMap.push([name, node.#varIndex as number])
}
} else {
node = this.children[token]
node = this.#children[token]
if (!node) {
if (
Object.keys(this.children).some(
Object.keys(this.#children).some(
(k) =>
k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR
)
@ -122,7 +122,7 @@ export class Node {
if (pathErrorCheckOnly) {
return
}
node = this.children[token] = new Node()
node = this.#children[token] = new Node()
}
}
@ -130,21 +130,21 @@ export class Node {
}
buildRegExpStr(): string {
const childKeys = Object.keys(this.children).sort(compareKey)
const childKeys = Object.keys(this.#children).sort(compareKey)
const strList = childKeys.map((k) => {
const c = this.children[k]
const c = this.#children[k]
return (
(typeof c.varIndex === 'number'
? `(${k})@${c.varIndex}`
(typeof c.#varIndex === 'number'
? `(${k})@${c.#varIndex}`
: regExpMetaChars.has(k)
? `\\${k}`
: k) + c.buildRegExpStr()
)
})
if (typeof this.index === 'number') {
strList.unshift(`#${this.index}`)
if (typeof this.#index === 'number') {
strList.unshift(`#${this.#index}`)
}
if (strList.length === 0) {

View File

@ -123,16 +123,17 @@ function findMiddleware<T>(
export class RegExpRouter<T> implements Router<T> {
name: string = 'RegExpRouter'
middleware?: Record<string, Record<string, HandlerWithMetadata<T>[]>>
routes?: Record<string, Record<string, HandlerWithMetadata<T>[]>>
#middleware?: Record<string, Record<string, HandlerWithMetadata<T>[]>>
#routes?: Record<string, Record<string, HandlerWithMetadata<T>[]>>
constructor() {
this.middleware = { [METHOD_NAME_ALL]: Object.create(null) }
this.routes = { [METHOD_NAME_ALL]: Object.create(null) }
this.#middleware = { [METHOD_NAME_ALL]: Object.create(null) }
this.#routes = { [METHOD_NAME_ALL]: Object.create(null) }
}
add(method: string, path: string, handler: T) {
const { middleware, routes } = this
const middleware = this.#middleware
const routes = this.#routes
if (!middleware || !routes) {
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT)
@ -232,14 +233,14 @@ export class RegExpRouter<T> implements Router<T> {
#buildAllMatchers(): Record<string, Matcher<T> | null> {
const matchers: Record<string, Matcher<T> | null> = Object.create(null)
Object.keys(this.routes!)
.concat(Object.keys(this.middleware!))
Object.keys(this.#routes!)
.concat(Object.keys(this.#middleware!))
.forEach((method) => {
matchers[method] ||= this.#buildMatcher(method)
})
// Release cache
this.middleware = this.routes = undefined
this.#middleware = this.#routes = undefined
return matchers
}
@ -249,7 +250,7 @@ export class RegExpRouter<T> implements Router<T> {
let hasOwnRoute = method === METHOD_NAME_ALL
;[this.middleware!, this.routes!].forEach((r) => {
;[this.#middleware!, this.#routes!].forEach((r) => {
const ownRoute = r[method]
? Object.keys(r[method]).map((path) => [path, r[method][path]])
: []

View File

@ -4,8 +4,8 @@ import { Node } from './node'
export type ReplacementMap = number[]
export class Trie {
context: Context = { varIndex: 0 }
root: Node = new Node()
#context: Context = { varIndex: 0 }
#root: Node = new Node()
insert(path: string, index: number, pathErrorCheckOnly: boolean): ParamAssocArray {
const paramAssoc: ParamAssocArray = []
@ -41,13 +41,13 @@ export class Trie {
}
}
this.root.insert(tokens, index, paramAssoc, this.context, pathErrorCheckOnly)
this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly)
return paramAssoc
}
buildRegExp(): [RegExp, ReplacementMap, ReplacementMap] {
let regexp = this.root.buildRegExpStr()
let regexp = this.#root.buildRegExpStr()
if (regexp === '') {
return [/^$/, [], []] // never match
}

View File

@ -3,27 +3,29 @@ import { MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError } from '../../ro
export class SmartRouter<T> implements Router<T> {
name: string = 'SmartRouter'
routers: Router<T>[] = []
routes?: [string, string, T][] = []
#routers: Router<T>[] = []
#routes?: [string, string, T][] = []
constructor(init: Pick<SmartRouter<T>, 'routers'>) {
Object.assign(this, init)
constructor(init: { routers: Router<T>[] }) {
this.#routers = init.routers
}
add(method: string, path: string, handler: T) {
if (!this.routes) {
if (!this.#routes) {
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT)
}
this.routes.push([method, path, handler])
this.#routes.push([method, path, handler])
}
match(method: string, path: string): Result<T> {
if (!this.routes) {
if (!this.#routes) {
throw new Error('Fatal error')
}
const { routers, routes } = this
const routers = this.#routers
const routes = this.#routes
const len = routers.length
let i = 0
let res
@ -42,8 +44,8 @@ export class SmartRouter<T> implements Router<T> {
}
this.match = router.match.bind(router)
this.routers = [router]
this.routes = undefined
this.#routers = [router]
this.#routes = undefined
break
}
@ -59,10 +61,10 @@ export class SmartRouter<T> implements Router<T> {
}
get activeRouter(): Router<T> {
if (this.routes || this.routers.length !== 1) {
if (this.#routes || this.#routers.length !== 1) {
throw new Error('No active router has been determined yet.')
}
return this.routers[0]
return this.#routers[0]
}
}

View File

@ -14,26 +14,26 @@ type HandlerParamsSet<T> = HandlerSet<T> & {
}
export class Node<T> {
methods: Record<string, HandlerSet<T>>[]
#methods: Record<string, HandlerSet<T>>[]
children: Record<string, Node<T>>
patterns: Pattern[]
order: number = 0
params: Record<string, string> = Object.create(null)
#children: Record<string, Node<T>>
#patterns: Pattern[]
#order: number = 0
#params: Record<string, string> = Object.create(null)
constructor(method?: string, handler?: T, children?: Record<string, Node<T>>) {
this.children = children || Object.create(null)
this.methods = []
this.#children = children || Object.create(null)
this.#methods = []
if (method && handler) {
const m: Record<string, HandlerSet<T>> = Object.create(null)
m[method] = { handler, possibleKeys: [], score: 0 }
this.methods = [m]
this.#methods = [m]
}
this.patterns = []
this.#patterns = []
}
insert(method: string, path: string, handler: T): Node<T> {
this.order = ++this.order
this.#order = ++this.#order
// eslint-disable-next-line @typescript-eslint/no-this-alias
let curNode: Node<T> = this
@ -44,8 +44,8 @@ export class Node<T> {
for (let i = 0, len = parts.length; i < len; i++) {
const p: string = parts[i]
if (Object.keys(curNode.children).includes(p)) {
curNode = curNode.children[p]
if (Object.keys(curNode.#children).includes(p)) {
curNode = curNode.#children[p]
const pattern = getPattern(p)
if (pattern) {
possibleKeys.push(pattern[1])
@ -53,14 +53,14 @@ export class Node<T> {
continue
}
curNode.children[p] = new Node()
curNode.#children[p] = new Node()
const pattern = getPattern(p)
if (pattern) {
curNode.patterns.push(pattern)
curNode.#patterns.push(pattern)
possibleKeys.push(pattern[1])
}
curNode = curNode.children[p]
curNode = curNode.#children[p]
}
const m: Record<string, HandlerSet<T>> = Object.create(null)
@ -68,11 +68,11 @@ export class Node<T> {
const handlerSet: HandlerSet<T> = {
handler,
possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),
score: this.order,
score: this.#order,
}
m[method] = handlerSet
curNode.methods.push(m)
curNode.#methods.push(m)
return curNode
}
@ -85,8 +85,8 @@ export class Node<T> {
params: Record<string, string>
): HandlerParamsSet<T>[] {
const handlerSets: HandlerParamsSet<T>[] = []
for (let i = 0, len = node.methods.length; i < len; i++) {
const m = node.methods[i]
for (let i = 0, len = node.#methods.length; i < len; i++) {
const m = node.#methods[i]
const handlerSet = (m[method] || m[METHOD_NAME_ALL]) as HandlerParamsSet<T>
const processedSet: Record<number, boolean> = {}
if (handlerSet !== undefined) {
@ -107,7 +107,7 @@ export class Node<T> {
search(method: string, path: string): [[T, Params][]] {
const handlerSets: HandlerParamsSet<T>[] = []
this.params = Object.create(null)
this.#params = Object.create(null)
// eslint-disable-next-line @typescript-eslint/no-this-alias
const curNode: Node<T> = this
@ -121,42 +121,42 @@ export class Node<T> {
for (let j = 0, len2 = curNodes.length; j < len2; j++) {
const node = curNodes[j]
const nextNode = node.children[part]
const nextNode = node.#children[part]
if (nextNode) {
nextNode.params = node.params
nextNode.#params = node.#params
if (isLast) {
// '/hello/*' => match '/hello'
if (nextNode.children['*']) {
if (nextNode.#children['*']) {
handlerSets.push(
...this.#getHandlerSets(
nextNode.children['*'],
nextNode.#children['*'],
method,
node.params,
node.#params,
Object.create(null)
)
)
}
handlerSets.push(
...this.#getHandlerSets(nextNode, method, node.params, Object.create(null))
...this.#getHandlerSets(nextNode, method, node.#params, Object.create(null))
)
} else {
tempNodes.push(nextNode)
}
}
for (let k = 0, len3 = node.patterns.length; k < len3; k++) {
const pattern = node.patterns[k]
for (let k = 0, len3 = node.#patterns.length; k < len3; k++) {
const pattern = node.#patterns[k]
const params = { ...node.params }
const params = { ...node.#params }
// Wildcard
// '/hello/*/foo' => match /hello/bar/foo
if (pattern === '*') {
const astNode = node.children['*']
const astNode = node.#children['*']
if (astNode) {
handlerSets.push(
...this.#getHandlerSets(astNode, method, node.params, Object.create(null))
...this.#getHandlerSets(astNode, method, node.#params, Object.create(null))
)
tempNodes.push(astNode)
}
@ -169,27 +169,27 @@ export class Node<T> {
const [key, name, matcher] = pattern
const child = node.children[key]
const child = node.#children[key]
// `/js/:filename{[a-z]+.js}` => match /js/chunk/123.js
const restPathString = parts.slice(i).join('/')
if (matcher instanceof RegExp && matcher.test(restPathString)) {
params[name] = restPathString
handlerSets.push(...this.#getHandlerSets(child, method, node.params, params))
handlerSets.push(...this.#getHandlerSets(child, method, node.#params, params))
continue
}
if (matcher === true || matcher.test(part)) {
params[name] = part
if (isLast) {
handlerSets.push(...this.#getHandlerSets(child, method, params, node.params))
if (child.children['*']) {
handlerSets.push(...this.#getHandlerSets(child, method, params, node.#params))
if (child.#children['*']) {
handlerSets.push(
...this.#getHandlerSets(child.children['*'], method, params, node.params)
...this.#getHandlerSets(child.#children['*'], method, params, node.#params)
)
}
} else {
child.params = params
child.#params = params
tempNodes.push(child)
}
}

View File

@ -4,25 +4,25 @@ import { Node } from './node'
export class TrieRouter<T> implements Router<T> {
name: string = 'TrieRouter'
node: Node<T>
#node: Node<T>
constructor() {
this.node = new Node()
this.#node = new Node()
}
add(method: string, path: string, handler: T) {
const results = checkOptionalParameter(path)
if (results) {
for (let i = 0, len = results.length; i < len; i++) {
this.node.insert(method, results[i], handler)
this.#node.insert(method, results[i], handler)
}
return
}
this.node.insert(method, path, handler)
this.#node.insert(method, path, handler)
}
match(method: string, path: string): Result<T> {
return this.node.search(method, path)
return this.#node.search(method, path)
}
}