feat(core): add @choojs/core — ESM port of choo with consolidated nano* internals
Same public API as choo 7.1.0 (use/route/start/mount/toString/emit; choo() callable with or without new, Choo exported). nanobus, nanorouter+wayfarer, nanolru+component cache, nanoraf, nanohref, nanotiming, document-ready and scroll-to-anchor are ported into lib/ with per-file attribution; nanoquery is replaced by URLSearchParams. The v7 node test suite is ported from tape to node:test and passes unchanged in behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014NgfSjHE11oFpoSnLVKLXd
This commit is contained in:
co-authored by
Claude Fable 5
parent
90cca611c9
commit
4ad3d02207
@@ -0,0 +1,161 @@
|
||||
// Ported from nanobus 4.5.0 (MIT) — https://github.com/choojs/nanobus
|
||||
// Event emitter with a '*' wildcard channel and nanotiming instrumentation.
|
||||
|
||||
import nanotiming from './timing.js'
|
||||
import { ok, equal } from './assert.js'
|
||||
|
||||
function assertEventName (eventName, method) {
|
||||
ok(
|
||||
typeof eventName === 'string' || typeof eventName === 'symbol',
|
||||
`nanobus.${method}: eventName should be type string or symbol`
|
||||
)
|
||||
}
|
||||
|
||||
export default class Nanobus {
|
||||
constructor (name) {
|
||||
this._name = name || 'nanobus'
|
||||
this._starListeners = []
|
||||
this._listeners = {}
|
||||
}
|
||||
|
||||
emit (eventName, ...data) {
|
||||
assertEventName(eventName, 'emit')
|
||||
|
||||
const emitTiming = nanotiming(this._name + "('" + eventName.toString() + "')")
|
||||
const listeners = this._listeners[eventName]
|
||||
if (listeners && listeners.length > 0) {
|
||||
this._emit(this._listeners[eventName], data)
|
||||
}
|
||||
|
||||
if (this._starListeners.length > 0) {
|
||||
this._emit(this._starListeners, eventName, data, emitTiming.uuid)
|
||||
}
|
||||
emitTiming()
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
on (eventName, listener) {
|
||||
assertEventName(eventName, 'on')
|
||||
equal(typeof listener, 'function', 'nanobus.on: listener should be type function')
|
||||
|
||||
if (eventName === '*') {
|
||||
this._starListeners.push(listener)
|
||||
} else {
|
||||
if (!this._listeners[eventName]) this._listeners[eventName] = []
|
||||
this._listeners[eventName].push(listener)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
addListener (eventName, listener) {
|
||||
return this.on(eventName, listener)
|
||||
}
|
||||
|
||||
prependListener (eventName, listener) {
|
||||
assertEventName(eventName, 'prependListener')
|
||||
equal(typeof listener, 'function', 'nanobus.prependListener: listener should be type function')
|
||||
|
||||
if (eventName === '*') {
|
||||
this._starListeners.unshift(listener)
|
||||
} else {
|
||||
if (!this._listeners[eventName]) this._listeners[eventName] = []
|
||||
this._listeners[eventName].unshift(listener)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
once (eventName, listener) {
|
||||
assertEventName(eventName, 'once')
|
||||
equal(typeof listener, 'function', 'nanobus.once: listener should be type function')
|
||||
|
||||
const self = this
|
||||
this.on(eventName, function once (...args) {
|
||||
listener.apply(self, args)
|
||||
self.removeListener(eventName, once)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
prependOnceListener (eventName, listener) {
|
||||
assertEventName(eventName, 'prependOnceListener')
|
||||
equal(typeof listener, 'function', 'nanobus.prependOnceListener: listener should be type function')
|
||||
|
||||
const self = this
|
||||
this.prependListener(eventName, function once (...args) {
|
||||
listener.apply(self, args)
|
||||
self.removeListener(eventName, once)
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
removeListener (eventName, listener) {
|
||||
assertEventName(eventName, 'removeListener')
|
||||
equal(typeof listener, 'function', 'nanobus.removeListener: listener should be type function')
|
||||
|
||||
if (eventName === '*') {
|
||||
this._starListeners = this._starListeners.slice()
|
||||
return remove(this._starListeners, listener)
|
||||
} else {
|
||||
if (typeof this._listeners[eventName] !== 'undefined') {
|
||||
this._listeners[eventName] = this._listeners[eventName].slice()
|
||||
}
|
||||
return remove(this._listeners[eventName], listener)
|
||||
}
|
||||
|
||||
function remove (arr, listener) {
|
||||
if (!arr) return
|
||||
const index = arr.indexOf(listener)
|
||||
if (index !== -1) {
|
||||
arr.splice(index, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeAllListeners (eventName) {
|
||||
if (eventName) {
|
||||
if (eventName === '*') {
|
||||
this._starListeners = []
|
||||
} else {
|
||||
this._listeners[eventName] = []
|
||||
}
|
||||
} else {
|
||||
this._starListeners = []
|
||||
this._listeners = {}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
listeners (eventName) {
|
||||
const listeners = eventName !== '*'
|
||||
? this._listeners[eventName]
|
||||
: this._starListeners
|
||||
|
||||
return listeners ? listeners.slice() : []
|
||||
}
|
||||
|
||||
_emit (arr, eventName, data, uuid) {
|
||||
if (typeof arr === 'undefined') return
|
||||
if (arr.length === 0) return
|
||||
if (data === undefined) {
|
||||
data = eventName
|
||||
eventName = null
|
||||
}
|
||||
|
||||
if (eventName) {
|
||||
if (uuid !== undefined) {
|
||||
data = [eventName].concat(data, uuid)
|
||||
} else {
|
||||
data = [eventName].concat(data)
|
||||
}
|
||||
}
|
||||
|
||||
// Take a copy in case a listener mutates the array while we iterate.
|
||||
const listeners = arr.slice()
|
||||
for (let i = 0; i < listeners.length; i++) {
|
||||
const listener = listeners[i]
|
||||
listener.apply(listener, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user