Add Biome for lint and code quality check #2
|
@ -19,10 +19,7 @@
|
||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true,
|
"recommended": true
|
||||||
"suspicious": {
|
|
||||||
"noExplicitAny": "off"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
|
|
83
src/main.ts
83
src/main.ts
|
@ -1,6 +1,6 @@
|
||||||
interface CustomElement extends HTMLElement {
|
interface CustomElement extends HTMLElement {
|
||||||
setState(key_path: string, value: any): void
|
setState(key_path: string, value: unknown): void
|
||||||
getState(key_path: string): any
|
getState(key_path: string): unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ComponentOptions {
|
interface ComponentOptions {
|
||||||
|
@ -14,9 +14,9 @@ interface ComponentOptions {
|
||||||
oldValue: string,
|
oldValue: string,
|
||||||
newValue: string,
|
newValue: string,
|
||||||
) => void
|
) => void
|
||||||
states?: Record<string, any>
|
states?: Record<string, unknown>
|
||||||
statesListeners?: { [key: string]: (value: any) => void }
|
statesListeners?: { [key: string]: (value: unknown) => void }
|
||||||
funcs?: { [key: string]: (...args: any[]) => void }
|
funcs?: { [key: string]: (...args: unknown[]) => void }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (options: ComponentOptions) => {
|
export default (options: ComponentOptions) => {
|
||||||
|
@ -35,10 +35,10 @@ export default (options: ComponentOptions) => {
|
||||||
componentRegistry.set(tag, options)
|
componentRegistry.set(tag, options)
|
||||||
|
|
||||||
class CustomElementImpl extends HTMLElement {
|
class CustomElementImpl extends HTMLElement {
|
||||||
private _states: Record<string, any> = {}
|
private _states: Record<string, unknown> = {}
|
||||||
private _stateToElementsMap: Record<string, Set<HTMLElement>> = {}
|
private _stateToElementsMap: Record<string, Set<HTMLElement>> = {}
|
||||||
private _currentRenderingElement: HTMLElement | null = null
|
private _currentRenderingElement: HTMLElement | null = null
|
||||||
private _statesListeners: Record<string, (...args: any[]) => void> = {}
|
private _statesListeners: Record<string, (...args: unknown[]) => void> = {}
|
||||||
private _textBindings: Array<{
|
private _textBindings: Array<{
|
||||||
node: Text
|
node: Text
|
||||||
expr: string
|
expr: string
|
||||||
|
@ -66,7 +66,7 @@ export default (options: ComponentOptions) => {
|
||||||
this._states = new Proxy(
|
this._states = new Proxy(
|
||||||
{ ...(states || {}) },
|
{ ...(states || {}) },
|
||||||
{
|
{
|
||||||
set: (target: Record<string, any>, keyPath: string, value: any) => {
|
set: (target: Record<string, unknown>, keyPath: string, value: unknown) => {
|
||||||
const valueRoute = keyPath.split('.')
|
const valueRoute = keyPath.split('.')
|
||||||
let currentTarget = target
|
let currentTarget = target
|
||||||
for (const i in valueRoute) {
|
for (const i in valueRoute) {
|
||||||
|
@ -74,10 +74,9 @@ export default (options: ComponentOptions) => {
|
||||||
if (Number.parseInt(i) === valueRoute.length - 1) {
|
if (Number.parseInt(i) === valueRoute.length - 1) {
|
||||||
currentTarget[key] = value
|
currentTarget[key] = value
|
||||||
} else {
|
} else {
|
||||||
if (!currentTarget[key]) {
|
if (!currentTarget[key])
|
||||||
currentTarget[key] = {}
|
currentTarget[key] = {}
|
||||||
}
|
currentTarget = currentTarget[key] as Record<string, unknown>
|
||||||
currentTarget = currentTarget[key]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// trigger dom updates
|
// trigger dom updates
|
||||||
|
@ -97,7 +96,7 @@ export default (options: ComponentOptions) => {
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
get: (target: Record<string, any>, keyPath: string) => {
|
get: (target: Record<string, unknown>, keyPath: string) => {
|
||||||
// collect state dependencies
|
// collect state dependencies
|
||||||
if (this._currentRenderingElement) {
|
if (this._currentRenderingElement) {
|
||||||
if (!this._stateToElementsMap[keyPath])
|
if (!this._stateToElementsMap[keyPath])
|
||||||
|
@ -116,7 +115,7 @@ export default (options: ComponentOptions) => {
|
||||||
|
|
||||||
if (!currentTarget[key])
|
if (!currentTarget[key])
|
||||||
currentTarget[key] = {}
|
currentTarget[key] = {}
|
||||||
currentTarget = currentTarget[key]
|
currentTarget = currentTarget[key] as Record<string, unknown>
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
|
@ -205,7 +204,7 @@ export default (options: ComponentOptions) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _updateElement(element: HTMLElement) {
|
private _updateElement(element: HTMLElement) {
|
||||||
const renderFunction = (element as any)._renderFunction
|
const renderFunction = (element as { _renderFunction?: () => string | Node })._renderFunction
|
||||||
if (renderFunction) {
|
if (renderFunction) {
|
||||||
// Set rendering context
|
// Set rendering context
|
||||||
this._currentRenderingElement = element
|
this._currentRenderingElement = element
|
||||||
|
@ -350,11 +349,11 @@ export default (options: ComponentOptions) => {
|
||||||
eventName,
|
eventName,
|
||||||
handlerValue,
|
handlerValue,
|
||||||
)
|
)
|
||||||
} else if (typeof (this as any)[handlerValue] === 'function') {
|
} else if (typeof (this as Record<string, unknown>)[handlerValue] === 'function') {
|
||||||
// Handle method reference: @click="handleClick"
|
// Handle method reference: @click="handleClick"
|
||||||
currentElementNode.addEventListener(
|
currentElementNode.addEventListener(
|
||||||
eventName,
|
eventName,
|
||||||
(this as any)[handlerValue].bind(this),
|
((this as unknown as Record<string, (...args: unknown[]) => void>)[handlerValue]).bind(this),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Handle simple expression: @click="count++" or @input="name = $event.target.value"
|
// Handle simple expression: @click="count++" or @input="name = $event.target.value"
|
||||||
|
@ -425,12 +424,11 @@ export default (options: ComponentOptions) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add event listener for state changes
|
// Add event listener for state changes
|
||||||
this._statesListeners[expr] = (newValue: any) => {
|
this._statesListeners[expr] = (newValue: unknown) => {
|
||||||
if (element instanceof HTMLInputElement) {
|
if (element instanceof HTMLInputElement)
|
||||||
element.value = newValue
|
element.value = newValue as string
|
||||||
} else {
|
else
|
||||||
element.setAttribute('data-laterano-connect', String(newValue))
|
element.setAttribute('data-laterano-connect', String(newValue))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,8 +480,8 @@ export default (options: ComponentOptions) => {
|
||||||
// Store current rendered items
|
// Store current rendered items
|
||||||
const renderedItems: Array<{
|
const renderedItems: Array<{
|
||||||
element: Element
|
element: Element
|
||||||
key: any
|
key: unknown
|
||||||
data: any
|
data: unknown
|
||||||
index: number
|
index: number
|
||||||
}> = []
|
}> = []
|
||||||
|
|
||||||
|
@ -620,10 +618,10 @@ export default (options: ComponentOptions) => {
|
||||||
// Recursively process the element and its children, applying the item context
|
// Recursively process the element and its children, applying the item context
|
||||||
private _processElementWithItemContext(
|
private _processElementWithItemContext(
|
||||||
element: Element,
|
element: Element,
|
||||||
itemContext: Record<string, any>,
|
itemContext: Record<string, unknown>,
|
||||||
) {
|
) {
|
||||||
// 1. Store the item context of the element so that subsequent updates can find it
|
// 1. Store the item context of the element so that subsequent updates can find it
|
||||||
; (element as any)._itemContext = itemContext
|
; (element as { _itemContext?: Record<string, unknown> })._itemContext = itemContext
|
||||||
|
|
||||||
// 2. Process bindings in text nodes
|
// 2. Process bindings in text nodes
|
||||||
const processTextNodes = (node: Node) => {
|
const processTextNodes = (node: Node) => {
|
||||||
|
@ -768,7 +766,7 @@ export default (options: ComponentOptions) => {
|
||||||
private _setupNestedListRendering(
|
private _setupNestedListRendering(
|
||||||
element: Element,
|
element: Element,
|
||||||
expr: string,
|
expr: string,
|
||||||
parentItemContext: Record<string, any>,
|
parentItemContext: Record<string, unknown>,
|
||||||
) {
|
) {
|
||||||
// Similar to _setupListRendering, but applies to nested situations
|
// Similar to _setupListRendering, but applies to nested situations
|
||||||
// Parse the expression (e.g., "subItem in item.subItems")
|
// Parse the expression (e.g., "subItem in item.subItems")
|
||||||
|
@ -837,11 +835,11 @@ export default (options: ComponentOptions) => {
|
||||||
// Evaluate expressions using the item context
|
// Evaluate expressions using the item context
|
||||||
private _evaluateExpressionWithItemContext(
|
private _evaluateExpressionWithItemContext(
|
||||||
expression: string,
|
expression: string,
|
||||||
itemContext: Record<string, any>,
|
itemContext: Record<string, unknown>,
|
||||||
index?: number,
|
index?: number,
|
||||||
itemVar?: string,
|
itemVar?: string,
|
||||||
indexVar?: string,
|
indexVar?: string,
|
||||||
): any {
|
): unknown {
|
||||||
try {
|
try {
|
||||||
// Check if the expression directly references the item variable
|
// Check if the expression directly references the item variable
|
||||||
if (itemVar && expression === itemVar) {
|
if (itemVar && expression === itemVar) {
|
||||||
|
@ -857,7 +855,7 @@ export default (options: ComponentOptions) => {
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
if (value === undefined || value === null)
|
if (value === undefined || value === null)
|
||||||
return undefined
|
return undefined
|
||||||
value = value[part]
|
value = (value as { [key: string]: unknown})[part]
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
@ -911,7 +909,7 @@ export default (options: ComponentOptions) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _evaluateExpression(expression: string): any {
|
private _evaluateExpression(expression: string): unknown {
|
||||||
try {
|
try {
|
||||||
// get the state keys and values
|
// get the state keys and values
|
||||||
if (this._states[expression] !== undefined)
|
if (this._states[expression] !== undefined)
|
||||||
|
@ -1012,12 +1010,12 @@ export default (options: ComponentOptions) => {
|
||||||
private _createHandlerContext(event: Event, element: Element) {
|
private _createHandlerContext(event: Event, element: Element) {
|
||||||
// Basic context, including state
|
// Basic context, including state
|
||||||
const context: {
|
const context: {
|
||||||
[key: string]: any
|
[key: string]: unknown
|
||||||
$event: Event
|
$event: Event
|
||||||
$el: Element
|
$el: Element
|
||||||
this: CustomElementImpl // Provide reference to the component instance
|
this: CustomElementImpl // Provide reference to the component instance
|
||||||
setState: (keyPath: string, value: any) => void
|
setState: (keyPath: string, value: unknown) => void
|
||||||
getState: (keyPath: string) => any
|
getState: (keyPath: string) => unknown
|
||||||
} = {
|
} = {
|
||||||
...this._states,
|
...this._states,
|
||||||
$event: event,
|
$event: event,
|
||||||
|
@ -1032,10 +1030,10 @@ export default (options: ComponentOptions) => {
|
||||||
// (name) => {
|
// (name) => {
|
||||||
for (const name of Object.getOwnPropertyNames(Object.getPrototypeOf(this)))
|
for (const name of Object.getOwnPropertyNames(Object.getPrototypeOf(this)))
|
||||||
if (
|
if (
|
||||||
typeof (this as any)[name] === 'function' &&
|
typeof (this as Record<string, unknown>)[name] === 'function' &&
|
||||||
name !== 'constructor'
|
name !== 'constructor'
|
||||||
)
|
)
|
||||||
context[name] = (this as any)[name].bind(this)
|
context[name] = (this as unknown as Record<string, (...args: unknown[]) => void>)[name].bind(this)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
}
|
}
|
||||||
|
@ -1147,16 +1145,15 @@ export default (options: ComponentOptions) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get nested state value
|
// Get nested state value
|
||||||
private _getNestedState(path: string): any {
|
private _getNestedState(path: string): unknown {
|
||||||
// Handle nested paths, such as "profile.name"
|
// Handle nested paths, such as "profile.name"
|
||||||
const parts = path.split('.')
|
const parts = path.split('.')
|
||||||
let result = this._states
|
let result = this._states
|
||||||
|
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
if (result === undefined || result === null) {
|
if (result === undefined || result === null)
|
||||||
return undefined
|
return undefined
|
||||||
}
|
result = (result as { [key: string]: Record<string, unknown> })[part]
|
||||||
result = result[part]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -1183,23 +1180,23 @@ export default (options: ComponentOptions) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// state manager
|
// state manager
|
||||||
setState(keyPath: string, value: any) {
|
setState(keyPath: string, value: unknown) {
|
||||||
this._states[keyPath] = value
|
this._states[keyPath] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
getState(keyPath: string): any {
|
getState(keyPath: string): unknown {
|
||||||
const parts = keyPath.split('.')
|
const parts = keyPath.split('.')
|
||||||
let result = this._states
|
let result = this._states
|
||||||
for (const part of parts) {
|
for (const part of parts) {
|
||||||
if (result === undefined || result === null)
|
if (result === undefined || result === null)
|
||||||
return undefined
|
return undefined
|
||||||
result = result[part]
|
result = (result as { [key: string]: Record<string, unknown> })[part]
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// function trigger
|
// function trigger
|
||||||
triggerFunc(eventName: string, ...args: any[]) {
|
triggerFunc(eventName: string, ...args: unknown[]) {
|
||||||
funcs?.[eventName]?.call(this, ...args)
|
funcs?.[eventName]?.call(this, ...args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user