diff --git a/eslint.config.js b/eslint.config.js index a64c122272..3d89bc3d63 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -80,13 +80,8 @@ export default tseslint.config( 'packages/react/runtime/src/backgroundSnapshot.ts', 'packages/react/runtime/src/compat/**', 'packages/react/runtime/src/debug/**', - 'packages/react/runtime/src/hydrate.ts', - 'packages/react/runtime/src/list.ts', 'packages/react/runtime/src/lynx-api.ts', - 'packages/react/runtime/src/lynx.ts', 'packages/react/runtime/src/opcodes.ts', - 'packages/react/runtime/src/snapshot.ts', - 'packages/react/runtime/src/utils.ts', // TODO: enable eslint for tools // tools @@ -275,6 +270,9 @@ export default tseslint.config( tsconfigRootDir: import.meta.dirname, }, }, + rules: { + '@typescript-eslint/consistent-generic-constructors': 'off', + }, }, // JavaScript-related { diff --git a/packages/react/runtime/src/hydrate.ts b/packages/react/runtime/src/hydrate.ts index 32d79139a0..bcd2a04421 100644 --- a/packages/react/runtime/src/hydrate.ts +++ b/packages/react/runtime/src/hydrate.ts @@ -217,7 +217,7 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio } let swap; - if (swap = options?.swap) { + if ((swap = options?.swap)) { swap[before.__id] = after.__id; } diff --git a/packages/react/runtime/src/list.ts b/packages/react/runtime/src/list.ts index 9e94b4cca8..b3342d000a 100644 --- a/packages/react/runtime/src/list.ts +++ b/packages/react/runtime/src/list.ts @@ -40,7 +40,7 @@ export function componentAtIndexFactory( throw new Error('childCtx not found'); } - const platformInfo = childCtx.__listItemPlatformInfo || {}; + const platformInfo = childCtx.__listItemPlatformInfo ?? {}; const uniqID = childCtx.type + (platformInfo['reuse-identifier'] ?? ''); const recycleSignMap = recycleMap.get(uniqID); @@ -97,7 +97,7 @@ export function componentAtIndexFactory( if (enableReuseNotification) { flushOptions.listReuseNotification = { listElement: list, - itemKey: platformInfo['item-key'], + itemKey: platformInfo['item-key']!, }; } __FlushElementTree(root, flushOptions); @@ -108,7 +108,7 @@ export function componentAtIndexFactory( if (enableReuseNotification) { flushOptions.listReuseNotification = { listElement: list, - itemKey: platformInfo['item-key'], + itemKey: platformInfo['item-key']!, }; } __FlushElementTree(root, flushOptions); @@ -161,6 +161,7 @@ export function componentAtIndexFactory( } export function enqueueComponentFactory(): EnqueueComponentCallback { + // eslint-disable-next-line unicorn/consistent-function-scoping const enqueueComponent = (_: FiberElement, listID: number, sign: number) => { const signMap = gSignMap[listID]; const recycleMap = gRecycleMap[listID]; @@ -173,7 +174,7 @@ export function enqueueComponentFactory(): EnqueueComponentCallback { return; } - const platformInfo = childCtx.__listItemPlatformInfo || {}; + const platformInfo = childCtx.__listItemPlatformInfo ?? {}; const uniqID = childCtx.type + (platformInfo['reuse-identifier'] ?? ''); if (!recycleMap.has(uniqID)) { diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts index 4f7069e2e7..b3a8b7232c 100644 --- a/packages/react/runtime/src/lynx-api.ts +++ b/packages/react/runtime/src/lynx-api.ts @@ -86,6 +86,7 @@ export const root: Root = { __root.__jsx = jsx; } else { __root.__jsx = jsx; + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument render(jsx, __root as any); if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') { // This is for cases where `root.render()` is called asynchronously, diff --git a/packages/react/runtime/src/lynx.ts b/packages/react/runtime/src/lynx.ts index c17fe52d54..109c8343ec 100644 --- a/packages/react/runtime/src/lynx.ts +++ b/packages/react/runtime/src/lynx.ts @@ -42,6 +42,7 @@ if (__PROFILE__) { if (__BACKGROUND__) { // Trick Preact and TypeScript to accept our custom document adapter. + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment options.document = document as any; setupBackgroundDocument(); injectTt(); diff --git a/packages/react/runtime/src/snapshot.ts b/packages/react/runtime/src/snapshot.ts index 30fe10f5b3..64207ec463 100644 --- a/packages/react/runtime/src/snapshot.ts +++ b/packages/react/runtime/src/snapshot.ts @@ -22,6 +22,7 @@ import { ListUpdateInfoRecording } from './listUpdateInfo.js'; import { __pendingListUpdates } from './pendingListUpdates.js'; import { DynamicPartType } from './snapshot/dynamicPartType.js'; import { snapshotDestroyList } from './snapshot/list.js'; +import type { PlatformInfo } from './snapshot/platformInfo.js'; import { unref } from './snapshot/ref.js'; import { isDirectOrDeepEqual } from './utils.js'; @@ -165,7 +166,7 @@ export const backgroundSnapshotInstanceManager: { } const spreadKey = res[2]; if (spreadKey) { - return ctx.__values![expIndex][spreadKey]; + return (ctx.__values![expIndex] as { [spreadKey]: unknown })[spreadKey]; } else { return ctx.__values![expIndex]; } @@ -261,7 +262,7 @@ export class SnapshotInstance { __values?: unknown[] | undefined; __current_slot_index = 0; __worklet_ref_set?: Set | Worklet>; - __listItemPlatformInfo?: any; + __listItemPlatformInfo?: PlatformInfo; constructor(public type: string, id?: number) { this.__snapshot_def = snapshotManager.values.get(type)!; @@ -270,7 +271,7 @@ export class SnapshotInstance { throw new Error('Snapshot not found: ' + type); } - id ||= snapshotInstanceManager.nextId -= 1; + id ??= snapshotInstanceManager.nextId -= 1; this.__id = id; snapshotInstanceManager.values.set(id, this); } @@ -579,23 +580,17 @@ export class SnapshotInstance { } setAttribute(key: string | number, value: any): void { - const helper = (index: number, oldValue: any, newValue: any) => { - if (isDirectOrDeepEqual(oldValue, newValue)) {} - else { - this.__snapshot_def.update![index]!(this, index, oldValue); - } - }; - if (key === 'values') { const oldValues = this.__values; - this.__values = value; + const values = value as unknown[]; + this.__values = values; if (oldValues) { - for (let index = 0; index < value.length; index++) { - helper(index, oldValues[index], value[index]); + for (let index = 0; index < values.length; index++) { + this.callUpdateIfNotDirectOrDeepEqual(index, oldValues[index], values[index]); } } else { - for (let index = 0; index < value.length; index++) { - helper(index, undefined, value[index]); + for (let index = 0; index < values.length; index++) { + this.callUpdateIfNotDirectOrDeepEqual(index, undefined, values[index]); } } return; @@ -603,7 +598,7 @@ export class SnapshotInstance { const index = typeof key === 'string' ? Number(key.slice(2)) : key; this.__values ??= []; - helper(index, this.__values[index], this.__values[index] = value); + this.callUpdateIfNotDirectOrDeepEqual(index, this.__values[index], this.__values[index] = value); } toJSON(): Omit & { children: SnapshotInstance[] | undefined } { @@ -614,4 +609,11 @@ export class SnapshotInstance { children: this.__firstChild ? this.childNodes : undefined, }; } + + callUpdateIfNotDirectOrDeepEqual(index: number, oldValue: any, newValue: any): void { + if (isDirectOrDeepEqual(oldValue, newValue)) {} + else { + this.__snapshot_def.update![index]!(this, index, oldValue); + } + } } diff --git a/packages/react/runtime/src/snapshot/platformInfo.ts b/packages/react/runtime/src/snapshot/platformInfo.ts index 9b626d70a1..59d59ee307 100644 --- a/packages/react/runtime/src/snapshot/platformInfo.ts +++ b/packages/react/runtime/src/snapshot/platformInfo.ts @@ -17,13 +17,24 @@ const platformInfoAttributes: Set = /* @__PURE__ */ new Set([ 'estimated-main-axis-size-px', ]); +export interface PlatformInfo { + 'reuse-identifier'?: string; + 'full-span'?: boolean; + 'item-key'?: string; + 'sticky-top'?: boolean; + 'sticky-bottom'?: boolean; + 'estimated-height'?: number; + 'estimated-height-px'?: number; + 'estimated-main-axis-size-px'?: number; +} + function updateListItemPlatformInfo( ctx: SnapshotInstance, index: number, oldValue: any, elementIndex: number, ): void { - const newValue = ctx.__listItemPlatformInfo = ctx.__values![index]; + const newValue = ctx.__listItemPlatformInfo = ctx.__values![index] as PlatformInfo; const list = ctx.parentNode; if (list?.__snapshot_def.isListHolder) { diff --git a/packages/react/runtime/src/utils.ts b/packages/react/runtime/src/utils.ts index b35c9c719e..d1dc6cb940 100644 --- a/packages/react/runtime/src/utils.ts +++ b/packages/react/runtime/src/utils.ts @@ -25,11 +25,11 @@ export function isSdkVersionGt(major: number, minor: number): boolean { } export function pick(obj: T, keys: Iterable): Pick { - const result: any = {}; + const result: Partial> = {}; for (const key of keys) { if (key in obj) { result[key] = obj[key]; } } - return result; + return result as Pick; }