diff --git a/types/chrome/index.d.ts b/types/chrome/index.d.ts index ea0e941baae5c4..ad335f7a19396f 100644 --- a/types/chrome/index.d.ts +++ b/types/chrome/index.d.ts @@ -346,9 +346,10 @@ declare namespace chrome { export function setIcon(details: TabIconDetails, callback: () => void): void; /** - * Sets the html document to be opened as a popup when the user clicks on the action's icon. + * Sets the HTML document to be opened as a popup when the user clicks on the action's icon. * * Can return its result via Promise. + * @since Chrome 96 */ export function setPopup(details: PopupDetails): Promise; export function setPopup(details: PopupDetails, callback: () => void): void; @@ -1165,6 +1166,7 @@ declare namespace chrome { * Clears websites' cache storage data. * * Can return its result via Promise in Manifest V3 or later since Chrome 96. + * @since Chrome 72 */ export function removeCacheStorage(options: RemovalOptions): Promise; export function removeCacheStorage(options: RemovalOptions, callback: () => void): void; @@ -2433,7 +2435,17 @@ declare namespace chrome { * Exactly one of `imageData` or `path` must be specified. Both are dictionaries mapping a number of pixels to an image representation. The image representation in `imageData` is an `ImageData` object; for example, from a `canvas` element, while the image representation in `path` is the path to an image file relative to the extension's manifest. If `scale` screen pixels fit into a device-independent pixel, the `scale * n` icon is used. If that scale is missing, another image is resized to the required size. */ export class SetIcon { - constructor(options?: { imageData?: ImageData | { [size: string]: ImageData } | undefined }); + constructor( + options: + | { + imageData: ImageData | { [index: number]: ImageData }; + path?: string | { [index: number]: string } | undefined; + } + | { + imageData?: ImageData | { [index: number]: ImageData } | undefined; + path: string | { [index: number]: string }; + }, + ); } /** Provides the Declarative Event API consisting of {@link events.Event.addRules addRules}, {@link events.Event.removeRules removeRules}, and {@link events.Event.getRules getRules}. */ @@ -3330,7 +3342,7 @@ declare namespace chrome { /** Indicates the data type of the option. The requested data type must match the real data type of the underlying option. */ type: `${OptionType}`; /** Indicates the value to set. Leave unset to request automatic setting for options that have `autoSettable` enabled. The data type supplied for `value` must match `type`. */ - value?: string | number | boolean | number; + value?: string | number | boolean | number[]; } /** @@ -3560,6 +3572,8 @@ declare namespace chrome { /** * Performs a document scan and returns a Promise that resolves with a {@link ScanResults} object. If a callback is passed to this function, the returned data is passed to it instead. * @param options An object containing scan parameters. + * + * Can return its result via Promise in Manifest V3 or later since Chrome 96. */ export function scan(options: ScanOptions): Promise; export function scan(options: ScanOptions, callback: (result: ScanResults) => void): void; @@ -4364,9 +4378,12 @@ declare namespace chrome { } /** - * Retrieves the network details of the device's default network. If the user is not affiliated or the device is not connected to a network, runtime.lastError will be set with a failure reason. + * Retrieves the network details of the device's default network. If the user is not affiliated or the device is not connected to a network, {@link runtime.lastError} will be set with a failure reason. * @param callback Called with the device's default network's NetworkDetails. + * + * Can return its result via Promise in Manifest V3 or later since Chrome 96. */ + export function getNetworkDetails(): Promise; export function getNetworkDetails(callback: (networkDetails: NetworkDetails) => void): void; } @@ -6308,7 +6325,7 @@ declare namespace chrome { * This API is different from identity.getAccounts in two ways. The information returned is available offline, and it only applies to the primary account for the profile. * @param details Profile options. * - * Can return its result via Promise since Chrome 105. + * Can return its result via Promise since Chrome 106. */ export function getProfileUserInfo(details?: ProfileDetails): Promise; export function getProfileUserInfo( @@ -6323,7 +6340,7 @@ declare namespace chrome { * If an access token is discovered to be invalid, it should be passed to removeCachedAuthToken to remove it from the cache. The app may then retrieve a fresh token with `getAuthToken`. * @param details Token information. * - * Can return its result via Promise since Chrome 105. + * Can return its result via Promise since Chrome 106. */ export function removeCachedAuthToken(details: InvalidTokenDetails): Promise; export function removeCachedAuthToken(details: InvalidTokenDetails, callback: () => void): void; @@ -7911,7 +7928,7 @@ declare namespace chrome { export enum ClientCertificateType { ECDSA_SIGN = "ecdsaSign", - RAS_SIGN = "rasSign", + RSA_SIGN = "rsaSign", } export interface SelectDetails { @@ -8806,7 +8823,7 @@ declare namespace chrome { export type QueryInfo = & { /** String to query with the default search provider. */ - text?: string | undefined; + text: string; } & ( | { @@ -12142,7 +12159,7 @@ declare namespace chrome { * Called when the list of {@link TtsVoice} that would be returned by getVoices has changed. * @since Chrome 124 */ - const onVoicesChanged: chrome.events.Event<() => void>; + export const onVoicesChanged: chrome.events.Event<() => void>; } //////////////////// @@ -13094,7 +13111,7 @@ declare namespace chrome { EXTRA_HEADERS = "extraHeaders", } - /** @since Chrome 44 */ + /** @since Chrome 79 */ export enum OnErrorOccurredOptions { /** Specifies that headers can violate Cross-Origin Resource Sharing (CORS). */ EXTRA_HEADERS = "extraHeaders", @@ -13286,14 +13303,14 @@ declare namespace chrome { extends SetPartial { /** Contains the HTTP request body data. Only provided if extraInfoSpec contains 'requestBody'. */ - requestBody: { + requestBody?: { /** Errors when obtaining request body data. */ error?: string; /** If the request method is POST and the body is a sequence of key-value pairs encoded in UTF8, encoded as either multipart/form-data, or application/x-www-form-urlencoded, this dictionary is present and for each key contains the list of all values for that key. If the data is of another media type, or if it is malformed, the dictionary is not present. An example value of this dictionary is {'key': \['value1', 'value2'\]}. */ formData?: { [key: string]: FormDataItem[] }; /** If the request method is PUT or POST, and the body is not already parsed in formData, then the unparsed request body elements are contained in this array. */ raw?: UploadData[]; - } | undefined; + }; } export interface OnBeforeSendHeadersDetails extends WebRequestDetails { @@ -14345,7 +14362,7 @@ declare namespace chrome { * The headers provided by a hypothetical response if the request does not get blocked or redirected before it is sent. Represented as an object which maps a header name to a list of string values. If not specified, the hypothetical response would return empty response headers, which can match rules which match on the non-existence of headers. E.g. `{"content-type": ["text/html; charset=utf-8", "multipart/form-data"]}` * @since Chrome 129 */ - responseHeaders?: { [name: string]: unknown }; + responseHeaders?: { [name: string]: string[] }; /** The ID of the tab in which the hypothetical request takes place. Does not need to correspond to a real tab ID. Default is -1, meaning that the request isn't related to a tab. */ tabId?: number; /** @@ -14683,13 +14700,13 @@ declare namespace chrome { * Fired when the extension's side panel is closed. * @since Chrome 142 */ - const onClosed: events.Event<(info: PanelClosedInfo) => void>; + export const onClosed: events.Event<(info: PanelClosedInfo) => void>; /** * Fired when the extension's side panel is opened. * @since Chrome 141 */ - const onOpened: events.Event<(info: PanelOpenedInfo) => void>; + export const onOpened: events.Event<(info: PanelOpenedInfo) => void>; } //////////////////// @@ -14795,7 +14812,7 @@ declare namespace chrome { /** Specifies wildcard patterns for pages this user script will be injected into. */ includeGlobs?: string[] | undefined; /** The list of ScriptSource objects defining sources of scripts to be injected into matching pages. This property must be specified for {@link register}, and when specified it must be a non-empty array.*/ - js: ScriptSource[]; + js?: ScriptSource[] | undefined; /** Specifies which pages this user script will be injected into. See Match Patterns for more details on the syntax of these strings. This property must be specified for {@link register}. */ matches?: string[] | undefined; /** Specifies when JavaScript files are injected into the web page. The preferred and default value is `document_idle` */ diff --git a/types/chrome/test/index.ts b/types/chrome/test/index.ts index 1f903a0736e583..02141adcc5aa73 100644 --- a/types/chrome/test/index.ts +++ b/types/chrome/test/index.ts @@ -1316,6 +1316,14 @@ function testDeclarativeContent() { const imageData = new ImageData(32, 32); new chrome.declarativeContent.SetIcon({ imageData }); // $ExpectType SetIcon + new chrome.declarativeContent.SetIcon({ imageData: { 32: imageData } }); // $ExpectType SetIcon + new chrome.declarativeContent.SetIcon({ path: "image.jpg" }); // $ExpectType SetIcon + new chrome.declarativeContent.SetIcon({ imageData, path: "image.jpg" }); // $ExpectType SetIcon + new chrome.declarativeContent.SetIcon({ imageData, path: { "32": "image.jpg" } }); // $ExpectType SetIcon + // @ts-expect-error Cannot use 'in' operator to search for 'iconIndex' in undefined + new chrome.declarativeContent.SetIcon(); + // @ts-expect-error Uncaught Error: Either the path or imageData property must be specified + new chrome.declarativeContent.SetIcon({}); const action = new chrome.declarativeContent.ShowAction(); // $ExpectType ShowAction @@ -4528,7 +4536,7 @@ function testDocumentScan() { const optionSettings: chrome.documentScan.OptionSetting[] = [{ name: "name", type: "GROUP", - value: "value", + value: [10], }]; chrome.documentScan.setOptions(scannerHandle, optionSettings); // $ExpectType Promise> chrome.documentScan.setOptions(scannerHandle, optionSettings, response => { // $ExpectType void @@ -4597,6 +4605,18 @@ function testEnterpriseHardwarePlatform() { chrome.enterprise.hardwarePlatform.getHardwarePlatformInfo((info) => {}).then((info) => {}); } +// https://developer.chrome.com/docs/extensions/reference/api/enterprise/networkingAttributes +function testEntrepriseNetworkingAttributes() { + chrome.enterprise.networkingAttributes.getNetworkDetails(); // $ExpectType Promise + chrome.enterprise.networkingAttributes.getNetworkDetails((networkAddresses) => { // $ExpectType void + networkAddresses.ipv4; // $ExpectType string | undefined + networkAddresses.ipv6; // $ExpectType string | undefined + networkAddresses.macAddress; // $ExpectType string + }); + // @ts-expect-error + chrome.enterprise.networkingAttributes.getNetworkDetails((networkAddresses) => {}).then((networkAddresses) => {}); +} + // https://developer.chrome.com/docs/extensions/reference/api/enterprise/login function testEnterpriseLogin() { chrome.enterprise.login.exitCurrentManagedGuestSession(); // $ExpectType Promise @@ -6626,7 +6646,7 @@ function testPrinterProvider() { // https://developer.chrome.com/docs/extensions/reference/api/platformKeys function testPlatformKeys() { chrome.platformKeys.ClientCertificateType.ECDSA_SIGN === "ecdsaSign"; - chrome.platformKeys.ClientCertificateType.RAS_SIGN === "rasSign"; + chrome.platformKeys.ClientCertificateType.RSA_SIGN === "rsaSign"; const arrayBuffer = new ArrayBuffer(0); @@ -6645,7 +6665,7 @@ function testPlatformKeys() { interactive: true, request: { certificateAuthorities: [], - certificateTypes: ["ecdsaSign", chrome.platformKeys.ClientCertificateType.RAS_SIGN], + certificateTypes: ["ecdsaSign", chrome.platformKeys.ClientCertificateType.RSA_SIGN], }, }; diff --git a/types/layui/index.d.ts b/types/layui/index.d.ts index 0eaa499b521541..7d57860069c9e7 100644 --- a/types/layui/index.d.ts +++ b/types/layui/index.d.ts @@ -1,20 +1,26 @@ /// +/// /// /// +/// /// /// /// /// /// /// +/// /// /// /// /// /// /// +/// +/// /// /// +/// /// /// /// @@ -28,19 +34,11 @@ declare const layui: Layui; declare const lay: Layui.LayStatic; declare const layer: Layui.Layer; -/** - * 动态加载等特殊场景设置 layui 目录 - * @since 2.6.6 - */ -declare const LAYUI_GLOBAL: { dir: string }; +declare const LAYUI_GLOBAL: Layui.GlobalProperties; interface Window { layui: Layui; lay: Layui.LayStatic; layer: Layui.Layer; - /** - * 动态加载等特殊场景设置 layui 目录 - * @since 2.6.6 - */ - LAYUI_GLOBAL: { dir: string }; + LAYUI_GLOBAL: Layui.GlobalProperties; } diff --git a/types/layui/misc.d.ts b/types/layui/misc.d.ts index 9551844821cdc1..1c9b14897d73bd 100644 --- a/types/layui/misc.d.ts +++ b/types/layui/misc.d.ts @@ -18,12 +18,33 @@ declare namespace Layui { }; type LiteralUnion = LiteralType | (BaseType & Record); type LiteralStringUnion = LiteralUnion; + type ObjectPathLeaf = Primitive | AnyFn | Date | Array; + type DeepPath = { + [K in keyof T & string]: T[K] extends ObjectPathLeaf ? `${Prefix}${K}` + : T[K] extends object ? DeepPath + : never; + }[keyof T & string]; + type DeepType = Path extends `${infer Key}.${infer Rest}` + ? Key extends keyof T ? DeepType + : T extends any[] ? DeepType + : never + : Path extends keyof T ? T[Path] + : T extends any[] ? T[number] + : never; + type DeepPartial = T extends AnyFn ? T + : T extends Date ? T + : T extends Array ? Array> + : T extends Map ? Map> + : T extends Set ? Set> + : T extends object ? { [P in keyof T]?: DeepPartial } + : T; type Selector = string; type ExportsCallback = (this: Layui, fn: (app: string, exports: object) => void) => void; /** - * 全局属性 + * 全局配置选项 + * layui.config(options) */ interface GlobalConfigOptions { /** @@ -44,6 +65,22 @@ declare namespace Layui { base?: string; } + /** + * window.LAYUI_GLOBAL + */ + interface GlobalProperties { + /** + * 动态加载等特殊场景设置 layui 目录 + * @since 2.6.6 + */ + dir?: string; + /** + * 国际化消息对象 + * @since 2.13.0 + */ + i18n?: I18nMessages; + } + /** * 缓存的所有数据 */ @@ -98,6 +135,12 @@ declare namespace Layui { * @internal */ all: any; + /** + * 面包屑 + * @see https://layui.dev/docs/2/nav/#separator + * @since 2.13.0 + */ + breadcrumb: Breadcrumb; /** * 轮播 * @see https://layui.dev/docs/2/carousel/ @@ -108,6 +151,12 @@ declare namespace Layui { * @see https://layui.dev/docs/2/code/ */ code: Code; + /** + * 折叠面板 + * @see https://layui.dev/docs/2/panel/ + * @since 2.13.0 + */ + collapse: Collapse; /** * 颜色选择器 * @see https://layui.dev/docs/2/colorpicker/ @@ -132,7 +181,7 @@ declare namespace Layui { * @see https://layui.dev/docs/2/progress/ - 进度条 * @see https://layui.dev/docs/2/nav/#separator - 面包屑 */ - element: Element; + element: LayElement; /** * 流加载 * @see https://layui.dev/docs/2/flow/ @@ -181,6 +230,18 @@ declare namespace Layui { * @internal */ "layui.all": string; + /** + * 导航栏 + * @see https://layui.dev/docs/2/nav/ + * @since 2.13.0 + */ + nav: Nav; + /** + * 进度条 + * @see https://layui.dev/docs/2/progress/ + * @since 2.13.0 + */ + progress: Progress; /** * 评分 * @see https://layui.dev/docs/2/rate/ @@ -192,6 +253,13 @@ declare namespace Layui { * @see https://layui.dev/docs/2/slider/ */ slider: Slider; + /** + * 选项卡 + * @see https://layui.dev/docs/2/tab/ + * @since 2.13.0 + * @deprecated 已废弃,使用 {@link Tabs} + */ + tab: Tab; /** * 表格 * @see https://layui.dev/docs/2/table @@ -230,6 +298,12 @@ declare namespace Layui { * @see https://layui.dev/docs/2/util/ */ util: Util; + /** + * 国际化 + * @see https://layui.dev/docs/2/i18n/ + * @since 2.12.0 + */ + i18n: I18n; } /** diff --git a/types/layui/modules/breadcrumb.d.ts b/types/layui/modules/breadcrumb.d.ts new file mode 100644 index 00000000000000..a994508792832d --- /dev/null +++ b/types/layui/modules/breadcrumb.d.ts @@ -0,0 +1,24 @@ +declare namespace Layui { + interface BreadcrumbOptions { + /** + * 组件渲染指定的目标元素选择器或 DOM 对象 + */ + elem: string | HTMLElement | JQuery; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class BreadcrumbClass extends Component { + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface BreadcrumbReturn extends ComponentReturn { + } + + interface Breadcrumb extends ComponentInterface { + /** + * 渲染面包屑组件 + * @param options 面包屑组件的配置选项 + */ + render(options: BreadcrumbOptions): BreadcrumbReturn; + } +} diff --git a/types/layui/modules/carousel.d.ts b/types/layui/modules/carousel.d.ts index ef6a21a81dfc62..6ab12e7f687f8b 100644 --- a/types/layui/modules/carousel.d.ts +++ b/types/layui/modules/carousel.d.ts @@ -91,7 +91,7 @@ declare namespace Layui { item: JQuery; } - interface CarouselClass { + class CarouselClass extends Component { /** * 配置属性 */ @@ -175,28 +175,34 @@ declare namespace Layui { goto(index: number): void; } + interface CarouselReturn extends ComponentReturn { + /** + * 切换到指定的索引 + * @param index 轮播下标,从 0 开始计算 + * @since 2.8.0 + */ + goto(index: number): void; + elemInd: any; + elemItem: any; + timer: any; + } + /** * 轮播 * @see https://layui.dev/docs/2/carousel/ + * @since 2.13.0 之后继承自 Component 组件 */ - interface Carousel { - config: Required; + interface Carousel extends ComponentInterface { /** * 渲染方法 * @param options 基础参数 - * @return 实例对象,包含操作当前实例的相关成员方法 */ - render(options: CarouselOptions): CarouselClass; + render(options: CarouselOptions): CarouselReturn; /** * 绑定切换事件 * @param event 事件 * @param callback 回调 */ on(event: string, callback: (this: CarouselClass, obj: CarouselItem) => any): any; - /** - * 设置轮播组件的全局参数 - * @param options 基础参数 - */ - set(options: Partial): this; } } diff --git a/types/layui/modules/code.d.ts b/types/layui/modules/code.d.ts index 28bf06ad3663b2..e23136f7e0fdaf 100644 --- a/types/layui/modules/code.d.ts +++ b/types/layui/modules/code.d.ts @@ -196,6 +196,55 @@ declare namespace Layui { * @since 2.8.18 */ code?: string; + /** + * 设置行高亮 + * @since 2.12.0 + */ + highlightLine?: { + /** + * 高亮 + */ + hl?: CodeHighlightLineOptions; + /** + * diff++ + */ + "++"?: CodeHighlightLineOptions; + /** + * diff-- + */ + "--"?: CodeHighlightLineOptions; + /** + * 聚焦 + */ + focus?: CodeHighlightLineOptions; + } & Record; + } + + interface CodeHighlightLineOptions { + /** + * 高亮范围,不可全局设置值 + * @example + * '1,3-5,8' + */ + range: string; + /** + * 是否解析行内的特定高亮注释。出于性能考虑,不建议全局开启。 + * + * 注释格式:`[!code :]` + * - `` : `highlightLine` 的可选项,如 `hl, focus` 等 + * - `` : 行数(含本行) + */ + comment?: boolean; + /** + * 添加到高亮行上的类 + * @internal + */ + classActiveLine?: string; + /** + * 有高亮行时向根元素添加的类 + * @internal + */ + classActivePre?: string; } interface CodeReturn { diff --git a/types/layui/modules/collapse.d.ts b/types/layui/modules/collapse.d.ts new file mode 100644 index 00000000000000..ce398cd9017d8a --- /dev/null +++ b/types/layui/modules/collapse.d.ts @@ -0,0 +1,34 @@ +declare namespace Layui { + interface CollapseOptions { + /** + * 组件渲染指定的目标元素选择器或 DOM 对象 + */ + elem: string | HTMLElement | JQuery; + } + + interface CollapseEventMap { + /** + * 折叠面板点击事件 + * @param data 事件对象 + * - `title` 折叠面板标题元素 + * - `content` 折叠面板内容元素 + * - `show` 折叠面板是否展开,true 为展开状态,false 为收起状态 + */ + collapse(this: HTMLElement, data: { title: JQuery; content: JQuery; show: boolean }): void; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class CollapseClass extends Component { + } + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface CollapseReturn extends ComponentReturn { + } + + interface Collapse extends ComponentInterface { + /** + * 渲染折叠面板组件 + * @param options 折叠面板组件的配置选项 + */ + render(options: CollapseOptions): CollapseReturn; + } +} diff --git a/types/layui/modules/colorpicker.d.ts b/types/layui/modules/colorpicker.d.ts index 992153e47f67c2..e659342df6e1b1 100644 --- a/types/layui/modules/colorpicker.d.ts +++ b/types/layui/modules/colorpicker.d.ts @@ -71,25 +71,16 @@ declare namespace Layui { /** * 颜色选择器 * @see https://layui.dev/docs/2/colorpicker/ + * @since 2.13.0 之后继承自 Component 组件 */ - interface ColorPicker { - config: Record; - index: number; + interface ColorPicker + extends + ComponentInterface, ComponentReturn> + { /** * colorpicker 组件渲染方法 * @param option 属性选项 */ - render(option: ColorPickerOptions): ColorPicker; - /** - * 全局设置 - * @param option 属性选项 - */ - set(option: ColorPickerOptions): ColorPicker; - /** - * 绑定切换事件 - * @param event 事件名 - * @param callback 回调函数 - */ - on(event: string, callback: (this: Layui, params: any) => any): any; + render(option: ColorPickerOptions): ComponentReturn; } } diff --git a/types/layui/modules/component.d.ts b/types/layui/modules/component.d.ts index bb6c3413b7ea0c..b3b9d99c6ec8de 100644 --- a/types/layui/modules/component.d.ts +++ b/types/layui/modules/component.d.ts @@ -81,12 +81,14 @@ declare namespace Layui { * 渲染函数 * @since 2.10.0 */ - render: ComponentBuilderOptions["render"]; + render( + ...args: ArgumentsType["render"]> + ): ReturnType["render"]>; /** * 事件绑定函数 * @since 2.10.0 */ - events: ComponentBuilderOptions["events"]; + events(...args: any[]): any; } interface ComponentReturn { diff --git a/types/layui/modules/dropdown.d.ts b/types/layui/modules/dropdown.d.ts index d71c2a4d855173..d2c13552f4820e 100644 --- a/types/layui/modules/dropdown.d.ts +++ b/types/layui/modules/dropdown.d.ts @@ -45,7 +45,7 @@ declare namespace Layui { /** * 绑定元素选择器或 DOM 对象 */ - elem?: string | Element | JQuery; + elem?: string | globalThis.Element | JQuery; /** * 菜单列数据项,也可用 {@link content|content} 选项自定义模板 */ diff --git a/types/layui/modules/element.d.ts b/types/layui/modules/element.d.ts index 1d7c1c3af63775..89ea7ee7f5ee87 100644 --- a/types/layui/modules/element.d.ts +++ b/types/layui/modules/element.d.ts @@ -1,153 +1,39 @@ declare namespace Layui { - interface TabOptions { - /** - * 选项卡的标题 - */ - title: string; - /** - * 选项卡的内容,支持传入 html 字符串 - */ - content: string; - /** - * 选项卡标题的 lay-id 属性值 - */ - id?: string; - /** - * 添加后自动切换 - * @default false - * @since 2.8.6 - */ - change?: boolean; - /** - * 是否开启删除图标 - * @default false - * @since 2.9.11 - */ - allowClose?: boolean; + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface LayElementEventMap extends TabEventMap, NavEventMap, CollapseEventMap { } - interface TabElement { - /** - * 指定tab头元素项 - */ - headerElem: string | HTMLElement | JQuery; - /** - * 指定tab主体元素项 - */ - bodyElem: string | HTMLElement | JQuery; + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class LayElementClass extends Component<{}> { } - interface ElementEventParam { - /** - * 当前 tab 项的所在下标 - */ - index: number; - /** - * 当前的 tab 容器 - */ - elem: JQuery; - /** - * 前的 tab 项 ID - * @since 2.9.11 - */ - id: string; + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface LayElementReturn extends ComponentReturn<{}> { } - interface ElementEventMap { - /** - * tab 切换事件 - */ - tab(this: HTMLElement, data: ElementEventParam): void; - /** - * tab 切换前的事件 - * 返回 false 阻止切换 - * @since 2.9.16 - */ - tabBeforeChange( - this: HTMLElement, - data: Pick & { - to: Omit; - from: Omit; - }, - ): undefined | boolean; - /** - * tab 删除事件 - */ - tabDelete(this: HTMLElement, data: ElementEventParam): void; - /** - * tab 删除前的事件 - * 返回 false 取消关闭操作 - * @since 2.9.11+ - */ - tabBeforeDelete(this: HTMLElement, data: ElementEventParam): undefined | boolean; - /** - * 导航栏点击事件 - * @param othis 点击元素的 jQuery 对象 - */ - nav(this: HTMLElement, othis: JQuery): void; - /** - * 折叠面板点击事件 - * @param data 事件对象 - * - `title` 折叠面板标题元素 - * - `content` 折叠面板内容元素 - * - `show` 折叠面板是否展开,true 为展开状态,false 为收起状态 - */ - collapse(this: HTMLElement, data: { title: JQuery; content: JQuery; show: boolean }): void; + + interface ProgressSetValue { + progress: Progress["setValue"]; } + /** - * 元素操作 + * 元素操作方法 + * @since 2.13.0 之后继承自 Component */ - interface Element { - config: Record; - /** - * 全局设置 - * @param options 选项 - */ - set(options: Record): this; + interface LayElement + extends + Omit, "render" | "on">, + Pick, + ProgressSetValue + { /** * 用于元素的一些事件触发 * @param event 事件名称 * @param callback 事件回调函数 */ - on(event: `${K}(${TableFilter})`, callback: ElementEventMap[K]): void; - on(event: K, callback: ElementEventMap[K]): void; + on(event: `${K}(${TableFilter})`, callback: LayElementEventMap[K]): this; + on(event: K, callback: LayElementEventMap[K]): this; // eslint-disable-next-line @definitelytyped/no-unnecessary-generics - on(event: `${K}(${TableFilter})`, callback: AnyFn): void; - /** - * 用于新增一个Tab选项 - * @param filter tab元素的 lay-filter="value" 过滤器的值 - * @param options 设定可选值的对象 - */ - tabAdd(filter: string, options: TabOptions): void; - /** - * 用于删除指定的Tab选项 - * @param filter tab 元素的 `lay-filter="value"` 过滤器的值 - * @param layid 选项卡标题列表的 lay-id 属性的值 - * @param force 是否强制删除,默认为false。如果为true,则会直接删除,不会触发事件 (2.9.21+) - */ - tabDelete(filter: string, layid: string, force?: boolean): void; - /** - * 用于外部切换到指定的Tab项上 - * @param filter 对应容器 lay-filter 的属性值 - * @param layid 比如:lay-id="xx"中的 'xx' - * @param force 是否强制切换,默认为false。如果为true,则会直接切换,不会触发事件 (2.9.15+) - */ - tabChange(filter: string, layid: string, force?: boolean): void; - /** - * 用于绑定自定义 Tab 元素(即非 layui 自带的 tab 结构) - * @param option 参数 - * @see https://layui.dev/docs/2/tab/ - */ - tab(option: TabElement): void; - /** - * 用于动态改变进度条百分比 - * @param filter 对应进度条容器 lay-filter 的属性值 - * @param percent 比例,百分比字符串,例如:'30%'、'50%' - * @example - * ```js - * element.progress('demo', '30%') - * ``` - */ - progress(filter: string, percent: string): void; + on(event: `${K}(${TableFilter})`, callback: AnyFn): this; /** * Element 渲染方法,用于重新渲染 Element 组件。 * @param type 渲染类型,未指定时将重新渲染页面内所有类型的 Element 组件 diff --git a/types/layui/modules/flow.d.ts b/types/layui/modules/flow.d.ts index 9e8e02e4a76976..102f88f2e98dc9 100644 --- a/types/layui/modules/flow.d.ts +++ b/types/layui/modules/flow.d.ts @@ -53,8 +53,9 @@ declare namespace Layui { /** * 流加载 * @see https://layui.dev/docs/2/flow/ + * @since 2.13.0 之后继承自 Component */ - interface Flow { + interface Flow extends ComponentInterface, ComponentReturn> { /** * 流加载 * @param option 信息流参数 diff --git a/types/layui/modules/i18n.d.ts b/types/layui/modules/i18n.d.ts new file mode 100644 index 00000000000000..7e9e389fee3efb --- /dev/null +++ b/types/layui/modules/i18n.d.ts @@ -0,0 +1,335 @@ +declare namespace Layui { + interface I18nOptions { + /** + * 语言环境,例如 'zh-CN' + * @default 'zh-CN' + */ + locale?: string; + /** + * 国际化消息对象 + */ + messages?: Record>; + } + + interface I18nMessages { + code: { + /** 复制按钮的提示文本 */ + copy: string; + /** 复制成功后的提示文本 */ + copied: string; + /** 复制失败后的提示文本 */ + copyError: string; + /** 最大化按钮的提示文本 */ + maximize: string; + /** 恢复大小按钮的提示文本 */ + restore: string; + /** 预览按钮的提示文本 */ + preview: string; + }; + colorpicker: { + /** 清除按钮的文本 */ + clear: string; + /** 确认按钮的文本 */ + confirm: string; + }; + dropdown: { + /** 无数据提示文本 */ + noData: string; + }; + flow: { + /** 加载更多按钮的文本 */ + loadMore: string; + /** 没有更多数据的提示文本 */ + noMore: string; + }; + form: { + /** 选择框的国际化消息 */ + select: { + /** 无数据提示文本 */ + noData: string; + /** 无匹配项提示文本 */ + noMatch: string; + /** 选择框的占位符文本 */ + placeholder: string; + }; + /** 表单验证的国际化消息 */ + validateMessages: { + /** 必填项提示文本 */ + required: string; + /** 手机号格式提示文本 */ + phone: string; + /** 邮箱格式提示文本 */ + email: string; + /** URL 格式提示文本 */ + url: string; + /** 数字格式提示文本 */ + number: string; + /** 日期格式提示文本 */ + date: string; + /** 身份证号格式提示文本 */ + identity: string; + }; + /** 验证错误时提示窗口的标题文本 */ + verifyErrorPromptTitle: string; + }; + laydate: { + /** 月份面板中月单元格和顶部导航栏的月份文本 */ + months: [ + January: string, + February: string, + March: string, + April: string, + May: string, + June: string, + July: string, + August: string, + September: string, + October: string, + November: string, + December: string, + ]; + /** 日期面板中周单元格的文本 */ + weeks: [ + Sunday: string, + Monday: string, + Tuesday: string, + Wednesday: string, + Thursday: string, + Friday: string, + Saturday: string, + ]; + /** 时间面板顶部的时、分、秒列表的标题文本 */ + time: [hour: string, minute: string, second: string]; + /** 字面量文本 */ + literal: { + /** 年面板中年单元格和顶部导航栏年相关的文本,数字和年份单位拼接时使用 */ + year: string; + }; + /** 日期和时间面板切换按钮的文本 */ + selectDate: string; + /** 时间和日期面板切换按钮的文本 */ + selectTime: string; + /** 开始时间 */ + startTime: string; + /** 结束时间 */ + endTime: string; + /** 工具按钮的文本 */ + tools: { + /** 确认按钮的文本 */ + confirm: string; + /** 清除按钮的文本 */ + clear: string; + /** 现在按钮的文本 */ + now: string; + /** 重置按钮的文本 */ + reset: string; + }; + /** 结束时间早于开始时间的提示文本 */ + rangeOrderPrompt: string; + /** 无效日期提示文本 */ + invalidDatePrompt: string; + /** 日期格式错误提示文本 */ + formatErrorPrompt: string; + /** 自动重置提示文本 */ + autoResetPrompt: string; + /** 选中结果预览文本 */ + preview: string; + }; + layer: { + /** 确认按钮的文本 */ + confirm: string; + /** 取消按钮的文本 */ + cancel: string; + /** 窗口默认标题的文本 */ + defaultTitle: string; + /** 提示输入框(layer.prompt)的国际化消息对象 */ + prompt: { + /** 超出输入长度后的提示文本 */ + inputLengthPrompt: string; + }; + /** layer.photos 的国际化消息 */ + photos: { + /** 无数据提示文本 */ + noData: string; + /** 工具按钮的国际化消息 */ + tools: { + /** 旋转按钮的提示文本 */ + rotate: string; + /** 水平变换按钮的提示文本 */ + scaleX: string; + /** 放大按钮的提示文本 */ + zoomIn: string; + /** 缩小按钮的提示文本 */ + zoomOut: string; + /** 重置按钮的提示文本 */ + reset: string; + /** 关闭按钮的提示文本 */ + close: string; + }; + /** 查看图片按钮的文本 */ + viewPicture: string; + /** URL 错误窗口的国际化消息 */ + urlError: { + /** 提示文本 */ + prompt: string; + /** 确认按钮的文本 */ + confirm: string; + /** 取消按钮的文本 */ + cancel: string; + }; + }; + }; + laypage: { + /** 上一页按钮的文本 */ + prev: string; + /** 下一页按钮的文本 */ + next: string; + /** 第一页按钮的文本 */ + first: string; + /** 最后一页按钮的文本 */ + last: string; + /** 总页数文本 */ + total: string; + /** 每页显示条数文本 */ + pagesize: string; + /** 跳转按钮的文本 */ + goto: string; + /** 当前页码文本 */ + page: string; + /** 确认按钮的文本 */ + confirm: string; + }; + table: { + /** 排序 */ + sort: { + /** 升序按钮的提示文本 */ + asc: string; + /** 降序按钮的提示文本 */ + desc: string; + }; + /** 无数据提示文本 */ + noData: string; + /** 工具按钮 */ + tools: { + filter: { + /** 筛选按钮的提示文本 */ + title: string; + }; + export: { + /** 导出按钮的提示文本 */ + title: string; + /** 无数据提示文本 */ + noDataPrompt: string; + /** 不兼容提示文本 */ + compatPrompt: string; + /** CSV 文本 */ + csvText: string; + }; + print: { + /** 打印按钮的提示文本 */ + title: string; + /** 无数据提示文本 */ + noDataPrompt: string; + }; + }; + /** 数据格式错误提示文本 */ + dataFormatError: string; + /** XHR 请求错误提示文本 */ + xhrError: string; + }; + transfer: { + /** 无数据提示文本 */ + noData: string; + /** 无匹配项提示文本 */ + noMatch: string; + /** 标题文本 */ + title: [list1: string, list2: string]; + /** 搜索框占位符文本 */ + searchPlaceholder: string; + }; + tree: { + /** 默认节点名称文本 */ + defaultNodeName: string; + /** 无数据提示文本 */ + noData: string; + /** 删除节点提示文本 */ + deleteNodePrompt: string; + }; + upload: { + /** 文件类型文本 */ + fileType: { + /** 文件 */ + file: string; + /** 图片 */ + image: string; + /** 视频 */ + video: string; + /** 音频 */ + audio: string; + }; + /** 验证消息文本 */ + validateMessages: { + /** 文件扩展名错误提示文本 */ + fileExtensionError: string; + /** 超过文件数量限制提示文本 */ + filesOverLengthLimit: string; + /** 当前文件数量文本 */ + currentFilesLength: string; + /** 文件大小超过限制提示文本 */ + fileOverSizeLimit: string; + }; + /** 文件按钮之后的文本 */ + chooseText: string; + }; + util: { + timeAgo: { + /** x 天前 */ + days: string; + /** x 小时前 */ + hours: string; + /** x 分钟前 */ + minutes: string; + /** 未来时间 */ + future: string; + /** 刚刚 */ + justNow: string; + }; + toDateString: { + /** + * 自定义时段 + * @see [CLDR 时段标准文档](https://www.unicode.org/cldr/charts/47/supplemental/day_periods.html) + */ + meridiem(hours: number, minutes: number): string; + }; + }; + } + + interface I18n { + config: Required; + set(options?: Partial): void; + /** + * 根据给定的键从国际化消息对象中获取翻译后的内容 + * + * 未文档化的私有方法,仅限内部使用 + * @internal + * @param keypath 语言包中的键路径,例如 'code.copy' + * @param parameters 占位符替换参数 + * @param options 选项,例如 {locale: 'zh-CN', default: '默认值'} + */ + translate

>>( + keypath: P, + parameters?: Record | any[], + options?: { locale?: string; default?: string }, + ): Layui.DeepType; + /** + * 根据给定的键从国际化消息对象中获取翻译后的内容 + * + * 翻译函数的别名,用于简化书写 + * @internal + * @param keypath 语言包中的键路径,例如 'code.copy' + * @param parameters 占位符替换参数 + * @param options 选项,例如 {locale: 'zh-CN', default: '默认值'} + */ + $t: I18n["translate"]; + } +} diff --git a/types/layui/modules/lay.d.ts b/types/layui/modules/lay.d.ts index 3fa20474261520..c704dd24e0ce4e 100644 --- a/types/layui/modules/lay.d.ts +++ b/types/layui/modules/lay.d.ts @@ -241,8 +241,76 @@ declare namespace Layui { * @default true */ capture?: boolean; + /** + * 是否检测 Iframe 点击事件 + * @since 2.12.0 + * @default false + */ + detectIframe?: boolean; + } + + interface LayTreeToFlatOptions { + /** + * 子节点字段名 + * @default 'children' + */ + childrenKey?: string; + /** + * 节点 id 字段名 + * @default 'id' + */ + idKey?: string; + /** + * 父节点 id 字段名 + * @default 'parentId' + */ + parentKey?: string; + /** + * 是否保留子节点数据 + * @default true + */ + keepChildren?: boolean; + } + + interface LayFlatToTreeOptions { + /** + * 子节点字段名 + * @default 'children' + */ + childrenKey?: string; + /** + * 节点 id 字段名 + * @default 'id' + */ + idKey?: string; + /** + * 父节点 id 字段名 + * @default 'parentId' + */ + parentKey?: string; } + type LayTreeNode = Record> = T & { + id?: string | number; + children?: LayTreeNode[]; + [key: string]: any; + }; + + type LayFlatNode = Record> = T & { + id?: string | number; + parentId?: string | number | null; + children?: LayTreeNode[]; + [key: string]: any; + }; + + type Customizer = ( + targetValue: any, + sourceValue: any, + key: string, + target: any, + source: any, + ) => any; + interface LayStatic { /** * 查找 DOM 作为返回实例的操作对象 @@ -264,9 +332,44 @@ declare namespace Layui { */ v: string; /** - * 把多个对象深度克隆到 target - * @param target 一个对象 - * @param objectN 包含额外的属性合并到第一个参数 + * 深拷贝合并多个对象 + * @param target 目标对象 + * @param objectN 源对象列表。最后一个参数可选为 customizer 函数(2.13.0+) + * @example + * ```js + * console.log(lay.extend({}, {a:1})); // expected: {a:1} + * console.log(lay.extend({a:1}, {a:3}, {a:5,b:5})); // expected: {a:5,b:5} + * // 多个相同源对象的不同合并方式 + * const objN = [ + * { + * a: [1, 3], + * b: {ba: 1} + * }, + * { + * a: [5], + * b: {bb: 2} + * }, + * { + * b: {ba: 3}, + * c: 3 + * } + * ]; + * console.log(lay.extend({}, ...objN)); // expected: {a:[5,3],b:{ba:3,bb:2},c:3} + * // 使用 customizer 实现数组覆盖而非合并 + * const obj1 = lay.extend({}, ...objN, function(objValue, srcValue) { + * if (Array.isArray(objValue) && Array.isArray(srcValue)) { + * return srcValue; + * } + * }); + * console.log(obj1); // expected: {a:[5],b:{ba:3,bb:2},c:3} + * // 使用 customizer 实现特定字段跳过合并 + * const obj2 = lay.extend({}, ...objN, function(objValue, srcValue, key, target, source) { + * if (key === 'b') { + * return objValue; + * } + * }); + * console.log(obj2); // expected: {a:[5,3],b:{ba:1},c:3} + * ``` */ extend( target: T, @@ -289,6 +392,7 @@ declare namespace Layui { extend(target: T, object1: U, object2: V, object3: W): T & U & V & W; extend(target: T, object1: U, object2: V): T & U & V; extend(target: T, object1: U): T & U; + extend(target: T, object1: U, customizer?: Customizer): T & U; extend(target: T): this & T; extend(target: any, object1: any, ...objectN: any[]): any; /** @@ -443,13 +547,9 @@ declare namespace Layui { clipboard: { /** * 剪贴板写入文本 - * @param text 写入剪贴板的文本 - * @param done 写入成功/完成回调 - * @param error 写入失败回调 - * * @since 2.8.17 */ - writeText(text: string, done?: () => void, error?: (err: any) => void): void; + writeText(options: { text: string; done?: () => void; error?: (err: any) => void }): void; }; /** * 检测是否支持 Passive Event Listeners @@ -469,11 +569,11 @@ declare namespace Layui { * @since 2.9.2 */ touchSwipe( - elem: string | Element | JQuery, + elem: string | globalThis.Element | JQuery, options: { - onTouchStart(e: TouchEvent, state: LayTouchSwipeState): void; - onTouchMove(e: TouchEvent, state: LayTouchSwipeState): void; - onTouchEnd(e: TouchEvent, state: LayTouchSwipeState): void; + onTouchStart?(e: TouchEvent, state: LayTouchSwipeState): void; + onTouchMove?(e: TouchEvent, state: LayTouchSwipeState): void; + onTouchEnd?(e: TouchEvent, state: LayTouchSwipeState): void; }, ): void; /** @@ -482,6 +582,7 @@ declare namespace Layui { * @param handler 事件触发时执行的函数 * @param options * @return 返回一个停止事件监听的函数 + * @since 2.9.13 */ onClickOutside< E extends keyof LayOnClickOutsideEventMap = "pointerdown", @@ -497,5 +598,90 @@ declare namespace Layui { * @param key 属性名 */ hasOwn(obj: O, key: K): obj is O & Record; // eslint-disable-line @definitelytyped/no-unnecessary-generics + /** + * 转义 HTML 字符串 + * @param str 待转义的字符串 + * @since 2.13.0 + */ + escape(str: string): string; + /** + * 还原转义 HTML 字符串 + * @param str 待还原转义的字符串 + * @since 2.13.0 + */ + unescape(str: string): string; + /** + * 创建一个共享的 ResizeObserver 实例 + * @param namespace 命名空间,用于标识不同的 ResizeObserver 实例 + * @returns 共享的 ResizeObserver 实例,或 null(如果不支持) + * @since 2.12.0 + */ + createSharedResizeObserver(namespace: string): { + disconnect(): void; + observe(target: globalThis.Element): void; + unobserve(target: globalThis.Element): void; + } | null; + /** + * 添加事件监听 + * @param elem 事件目标元素 + * @param type 事件类型 + * @param handler 事件处理函数 + * @param options 事件监听选项 + * @since 2.12.0 + */ + addEvent( + elem: globalThis.Element | Document | Window, + type: string, + handler: EventListenerOrEventListenerObject, + options?: AddEventListenerOptions | boolean, + ): any; + /** + * 移除事件监听 + * @param elem 事件目标元素 + * @param type 事件类型 + * @param handler 事件处理函数 + * @param options 事件监听选项 + * @since 2.13.0 + */ + removeEvent( + elem: globalThis.Element | Document | Window, + type: string, + handler: EventListenerOrEventListenerObject, + options?: EventListenerOptions | boolean, + ): any; + /** + * 将树状结构转换为扁平结构 + * @param data 树状结构数组 + * @param options 转换选项 + * @returns 扁平结构数组 + * @since 2.13.0 + */ + treeToFlat( + data: T[], + options?: LayTreeToFlatOptions, + ): LayFlatNode[]; + /** + * 将扁平结构转换为树状结构 + * @param data 扁平结构数组 + * @param options 转换选项 + * @returns 树状结构数组 + * @since 2.13.0 + */ + flatToTree( + data: T[], + options?: LayFlatToTreeOptions, + ): LayTreeNode[]; + /** + * 自动递增器,一般用于组件自增索引 + * @internal + * @since 2.13.0 + */ + autoIncrementer(key: string, options?: { target?: HTMLElement }): number; + /** + * 判断一个对象是否为普通对象(非数组、函数等) + * @param obj 对象 + * @since 2.13.0 + */ + isPlainObject(obj: any): obj is Record; } } diff --git a/types/layui/modules/layer.d.ts b/types/layui/modules/layer.d.ts index 53223d33d80fad..95634e8179560e 100644 --- a/types/layui/modules/layer.d.ts +++ b/types/layui/modules/layer.d.ts @@ -548,9 +548,10 @@ declare namespace Layui { * - 0 文本输入框 * - 1 密令输入框 * - 2 多行文本输入框 + * - [HTMLInputElement.type](https://developer.mozilla.org/zh-CN/docs/Web/HTML/Reference/Elements/input#input_%E7%B1%BB%E5%9E%8B%22) 值(2.13.0+) * @default 0 */ - formType?: 0 | 1 | 2; + formType?: 0 | 1 | 2 | HTMLInputElement["type"]; /** * 输入框初始值 * @default '' @@ -906,7 +907,7 @@ declare namespace Layui { * @param selector iframe 子页面的选择器或元素对象 * @param index 打开弹层时返回的唯一索引 */ - getChildFrame(selector: string | Element | JQuery, index: number): JQuery; + getChildFrame(selector: string | globalThis.Element | JQuery, index: number): JQuery; /** * 在 iframe 页中获取弹层索引 * @param windowName 即 window.name diff --git a/types/layui/modules/nav.d.ts b/types/layui/modules/nav.d.ts new file mode 100644 index 00000000000000..bd6f9988709aae --- /dev/null +++ b/types/layui/modules/nav.d.ts @@ -0,0 +1,32 @@ +declare namespace Layui { + interface NavOptions { + /** + * 组件渲染指定的目标元素选择器或 DOM 对象 + */ + elem: string | HTMLElement | JQuery; + } + + interface NavEventMap { + /** + * 导航栏点击事件 + * @param othis 点击元素的 jQuery 对象 + */ + nav(this: HTMLElement, othis: JQuery): void; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class NavClass extends Component { + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface NavReturn extends ComponentReturn { + } + + interface Nav extends ComponentInterface { + /** + * 渲染导航栏组件 + * @param options 导航栏组件的配置选项 + */ + render(options: NavOptions): NavReturn; + } +} diff --git a/types/layui/modules/progress.d.ts b/types/layui/modules/progress.d.ts new file mode 100644 index 00000000000000..905e7724e6b2ff --- /dev/null +++ b/types/layui/modules/progress.d.ts @@ -0,0 +1,29 @@ +declare namespace Layui { + interface ProgressOptions { + /** + * 组件渲染指定的目标元素选择器或 DOM 对象 + */ + elem: string | HTMLElement | JQuery; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class ProgressClass extends Component { + } + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface ProgressReturn extends ComponentReturn { + } + + interface Progress extends ComponentInterface { + /** + * 渲染进度条组件 + * @param options 进度条组件的配置选项 + */ + render(options: ProgressOptions): ProgressReturn; + /** + * 用于动态改变进度条百分比 + * @param filter 对应进度条容器 lay-filter 的属性值 + * @param percent 比例,百分比字符串,例如:'30%'、'50%' + */ + setValue(filter: string, percent: string): this; + } +} diff --git a/types/layui/modules/slider.d.ts b/types/layui/modules/slider.d.ts index e84487ea2addbe..3fa4b27a6bbe3e 100644 --- a/types/layui/modules/slider.d.ts +++ b/types/layui/modules/slider.d.ts @@ -87,31 +87,49 @@ declare namespace Layui { } /** - * 滑块 - * @see https://layui.dev/docs/2/slider/ + * 组件内部实例的详细类型请查看源码 */ - interface Slider { - config: Record; - index: number; + class SliderClass extends Component { + precision(...args: any[]): any; + slide(...args: any[]): any; + } + + interface SliderReturn extends ComponentReturn { /** - * 设置滑块的全局参数 - * @param options 基础参数 + * 改变指定滑块实例的数值 + * @param value 要设置的滑块数值 + * @param index 滑块所在的区间开始值或结尾值的索引,开始值:0 ; 结尾值:1 */ - set(options?: Partial): Slider; - on(event: string, callback: (obj: any) => any): any; + setValue(value: any, index?: number): void; + } + + /** + * 滑块 + * + * 该组件继承自 Component(2.12.0) + * @see https://layui.dev/docs/2/slider/ + */ + interface Slider extends ComponentInterface { /** * 核心方法 * @param option 参数 * @since 2.8.0+ 除 elem 属性外,其他基础属性也可以直接写在元素的 `lay-options="{}"` 属性中 */ - render(option: SliderOptions): { - config: Required; - /** - * 改变指定滑块实例的数值 - * @param value 要设置的滑块数值 - * @param index 滑块所在的区间开始值或结尾值的索引,开始值:0 ; 结尾值:1 - */ - setValue(value: any, index?: number): void; - }; + render(option: SliderOptions): SliderReturn; + /** + * 组件常量集 + * @since 2.11.0 + */ + CONST: { + ELEM_VIEW: string; + SLIDER_BAR: string; + SLIDER_WRAP: string; + SLIDER_WRAP_BTN: string; + SLIDER_TIPS: string; + SLIDER_INPUT: string; + SLIDER_INPUT_TXT: string; + SLIDER_INPUT_BTN: string; + ELEM_HOVER: string; + } & ComponentInterface["CONST"]; } } diff --git a/types/layui/modules/tab.d.ts b/types/layui/modules/tab.d.ts new file mode 100644 index 00000000000000..a91e7effb9a22b --- /dev/null +++ b/types/layui/modules/tab.d.ts @@ -0,0 +1,133 @@ +declare namespace Layui { + interface TabOptions { + /** + * 组件渲染指定的目标元素选择器或 DOM 对象 + */ + elem: string | HTMLElement | JQuery; + } + + interface TabEventParam { + /** + * 当前 tab 项的所在下标 + */ + index: number; + /** + * 当前的 tab 容器 + */ + elem: JQuery; + /** + * 前的 tab 项 ID + * @since 2.9.11 + */ + id: string; + } + + interface TabEventMap { + /** + * tab 切换事件 + */ + tab(this: HTMLElement, data: TabEventParam): void; + /** + * tab 切换前的事件 + * 返回 false 阻止切换 + * @since 2.9.16 + */ + tabBeforeChange( + this: HTMLElement, + data: Pick & { + to: Omit; + from: Omit; + }, + ): undefined | boolean; + /** + * tab 删除事件 + */ + tabDelete(this: HTMLElement, data: TabEventParam): void; + /** + * tab 删除前的事件 + * 返回 false 取消关闭操作 + * @since 2.9.11+ + */ + tabBeforeDelete(this: HTMLElement, data: TabEventParam): undefined | boolean; + } + + interface TabAddOptions { + /** + * 选项卡的标题 + */ + title: string; + /** + * 选项卡的内容,支持传入 html 字符串 + */ + content: string; + /** + * 选项卡标题的 lay-id 属性值 + */ + id?: string; + /** + * 添加后自动切换 + * @default false + * @since 2.8.6 + */ + change?: boolean; + /** + * 是否开启删除图标 + * @default false + * @since 2.9.11 + */ + allowClose?: boolean; + } + + interface TabElement { + /** + * 指定tab头元素项 + */ + headerElem: string | HTMLElement | JQuery; + /** + * 指定tab主体元素项 + */ + bodyElem: string | HTMLElement | JQuery; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class TabClass extends Component { + } + + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface TabReturn extends ComponentReturn { + } + + interface Tab extends ComponentInterface { + /** + * 渲染选项卡组件 + * @param options 选项卡组件的配置选项 + */ + render(options: TabOptions): TabReturn; + /** + * 用于新增一个Tab选项 + * @param filter tab元素的 lay-filter="value" 过滤器的值 + * @param options 设定可选值的对象 + */ + tabAdd(filter: string, options: TabAddOptions): this; + /** + * 用于删除指定的Tab选项 + * @param filter tab 元素的 `lay-filter="value"` 过滤器的值 + * @param layid 选项卡标题列表的 lay-id 属性的值 + * @param force 是否强制删除,默认为false。如果为true,则会直接删除,不会触发事件 (2.9.21+) + */ + tabDelete(filter: string, layid: string, force?: boolean): this; + /** + * 用于外部切换到指定的Tab项上 + * @param filter 对应容器 lay-filter 的属性值 + * @param layid 比如:lay-id="xx"中的 'xx' + * @param force 是否强制切换,默认为false。如果为true,则会直接切换,不会触发事件 (2.9.15+) + */ + tabChange(filter: string, layid: string, force?: boolean): this; + /** + * 用于绑定自定义 Tab 元素(即非 layui 自带的 tab 结构) + * @param options 选项 + * @see https://layui.dev/docs/2/tab/ + */ + tab(options: TabElement): void; + } +} diff --git a/types/layui/modules/table.d.ts b/types/layui/modules/table.d.ts index f5c207445c8b01..4680a25dd2b6ba 100644 --- a/types/layui/modules/table.d.ts +++ b/types/layui/modules/table.d.ts @@ -642,6 +642,21 @@ declare namespace Layui { * @deprecated 2.8.0 已弃用,请使用 {@link TableOptions.parseData|parseData} */ response?: TableResponseRename; + /** + * 是否强制计算表格主区域的行高度并同步到固定列区域。 + * + * 开启后会对表格性能有一定的影响,仅适用于行高度自适应的场景。 + * @default false + * @since 2.12.0 + */ + syncFixedRowHeight?: boolean; + /** + * 自定义 ajax 请求 + * @param options 原始的 jQuery.ajax 配置项 + * @param type 请求类型,`table` 表示普通表格数据请求,`treeNodes` 表示树表格节点数据请求 + * @since 2.12.0 + */ + ajax?(options: JQuery.AjaxSettings, type: "table" | "treeNodes"): void; } /** diff --git a/types/layui/modules/tabs.d.ts b/types/layui/modules/tabs.d.ts index 12e6b9e9956f9b..2f53714550c29a 100644 --- a/types/layui/modules/tabs.d.ts +++ b/types/layui/modules/tabs.d.ts @@ -7,7 +7,7 @@ declare namespace Layui { /** * 组件渲染的唯一实例 ID */ - id: string; + id?: string; /** * 给主容器追加 CSS 类名,以便自定义样式 */ diff --git a/types/layui/modules/transfer.d.ts b/types/layui/modules/transfer.d.ts index bd3dafb659e670..811bbfe461f9dd 100644 --- a/types/layui/modules/transfer.d.ts +++ b/types/layui/modules/transfer.d.ts @@ -88,33 +88,23 @@ declare namespace Layui { dblclick?(obj: { elem: JQuery; data: object; index: number }): boolean | undefined; } - interface TransferReturn { - config: Record; + // eslint-disable-next-line @typescript-eslint/no-empty-interface + class TransferClass extends Component { + } + + interface TransferReturn extends ComponentReturn { /** * 获得右侧数据 */ getData(): Array; - /** - * 重载实例 - * @param id 实例唯一索引 - * @param options 各项基础参数 - */ - reload(id: string, options: TransferOptions): void; - /** - * 设定全局默认参数 - * - * @param options 各项基础参数 - */ - set(options: Partial): void; } /** * 穿梭框 * @see https://layui.dev/docs/2/transfer/ + * @since 2.13.0 之后继承自 Component */ - interface Transfer { - config: Record; - index: number; + interface Transfer extends ComponentInterface { /** * 获得右侧数据 * @param id 实例唯一索引 @@ -125,22 +115,5 @@ declare namespace Layui { * @param option 各项基础参数 */ render(option: TransferOptions): TransferReturn; - /** - * 绑定事件,内部 modName 默认为 transfer,绑定参考 layui.onevent,触发参考 layui.event - * @param events - * @param callback - */ - on(events: string, callback: (this: Layui, obj: any) => any): any; - /** - * 重载实例 - * @param id 实例唯一索引 - * @param options 各项基础参数 - */ - reload(id: string, options?: Partial): void; - /** - * 设定全局默认参数 - * @param options 各项基础参数 - */ - set(options: Partial): void; } } diff --git a/types/layui/modules/tree.d.ts b/types/layui/modules/tree.d.ts index 18458d8e40e2ba..ac22c4625ff3f2 100644 --- a/types/layui/modules/tree.d.ts +++ b/types/layui/modules/tree.d.ts @@ -56,8 +56,6 @@ declare namespace Layui { type: LiteralStringUnion<"add" | "update" | "del">; } - type TreeReloadReturn = Pick; - interface TreeOptions { /** * 绑定元素选择器 @@ -160,48 +158,49 @@ declare namespace Layui { dragend?(...args: any): any; } + class TreeClass extends Component { + reload(...args: any[]): any; + renderForm(...args: any[]): any; + tree(...args: any[]): any; + spread(...args: any[]): any; + updateFieldValue(...args: any[]): any; + syncCheckedState(...args: any[]): any; + checkClick(...args: any[]): any; + operate(...args: any[]): any; + getChecked(...args: any[]): any; + setChecked(...args: any[]): any; + } + + interface TreeReturn extends ComponentReturn { + /** + * 获取选中的节点数据 + */ + getChecked(): ReturnType; + /** + * 设置选中的节点 + * @param id 对应 tree 渲染时定义的 id 属性值 + */ + setChecked(id: string): void; + } + /** * 树 * @see https://layui.dev/docs/2/tree/ + * @since 2.13.0 之后继承自 Component */ - interface Tree { - /** - * 全局参数项 - */ - config: Record; + interface Tree extends ComponentInterface { /** * 获取选中的节点数据 * @param id 对应 tree 渲染时定义的 id 属性值 */ getChecked(id: string): TreeData[]; /** - * tree 实例数 - */ - index: number; - /** - * 绑定事件,内部 modName 默认为 tree,绑定参考 layui.onevent,触发参考 layui.event - * @param events - * @param callback - */ - on(events: string, callback: (this: Layui, obj: any) => any): any; - /** - * 实例重载,重载一个已经创建的组件实例,覆盖基础属性 - * @param id 对应 tree 渲染时定义的 id 属性值 - * @param options 基础参数 - */ - reload(id: string, options: Partial): TreeReloadReturn; - /** - * 核心方法 + * 核心渲染方法 * @param option 基础参数 */ - render(option: TreeOptions): any; - /** - * 设置 tree全局 参数(预设基础参数值) - * @param option - */ - set(option?: Partial): Tree; + render(option: TreeOptions): TreeReturn; /** - * 设置节点勾选 + * 设置选中的节点 * @param id 对应 tree 渲染时定义的 id 属性值 * @param nodeId 对应 tree 渲染时的 data 中的 id 属性值。数组格式,可设置多个 */ diff --git a/types/layui/package.json b/types/layui/package.json index fa57b2725ecf40..eee4466a996b81 100644 --- a/types/layui/package.json +++ b/types/layui/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@types/layui", - "version": "2.11.9999", + "version": "2.13.9999", "projects": [ "https://github.com/layui/layui" ], diff --git a/types/layui/test/breadcrumb.test.ts b/types/layui/test/breadcrumb.test.ts new file mode 100644 index 00000000000000..ce25b724990659 --- /dev/null +++ b/types/layui/test/breadcrumb.test.ts @@ -0,0 +1,8 @@ +function breadcrumbTest() { + layui.use("breadcrumb", () => { + const breadcrumb = layui.breadcrumb; + breadcrumb.render({ + elem: "#breadcrumb", + }); + }); +} diff --git a/types/layui/test/code.test.ts b/types/layui/test/code.test.ts index 3cb664fc7f706a..9c43772108765b 100644 --- a/types/layui/test/code.test.ts +++ b/types/layui/test/code.test.ts @@ -37,6 +37,17 @@ function code() { style: "shiki", tools: ["full", "window", { title: ["复制"], type: "copy", event: (obj) => {} }], wordWrap: true, + highlightLine: { + hl: { + range: "1,3-5,8", + comment: true, + classActiveLine: "layui-code-active-line", + classActivePre: "layui-code-active-pre", + }, + customHl: { + range: "2,4-6,9", + }, + }, }); inst.reloadCode({ diff --git a/types/layui/test/collapse.test.ts b/types/layui/test/collapse.test.ts new file mode 100644 index 00000000000000..d81d6d84e9b6c7 --- /dev/null +++ b/types/layui/test/collapse.test.ts @@ -0,0 +1,8 @@ +function collapseTest() { + layui.use("collapse", () => { + const collapse = layui.collapse; + collapse.render({ + elem: "#collapse", + }); + }); +} diff --git a/types/layui/test/element.test.ts b/types/layui/test/element.test.ts index f763ed1b3bb983..420de67b36ac85 100644 --- a/types/layui/test/element.test.ts +++ b/types/layui/test/element.test.ts @@ -1,7 +1,7 @@ function elementTest() { layui.use("element", () => { const element = layui.element; - element.config["notExists"]; + element.config; // 一些事件监听 element.on("tab(demo)", data => { console.log(data); @@ -38,8 +38,8 @@ function elementTest() { console.log(data.elem); // 得到当前的Tab大容器 console.log(data.id); }); - element.on("tabDelete(filter)", data => { - // console.log(this); // 当前Tab标题所在的原始DOM元素 + element.on("tabDelete(filter)", function(data) { + console.log(this); // 当前Tab标题所在的原始DOM元素 console.log(data.index); // 得到当前Tab的所在下标 console.log(data.elem); // 得到当前的Tab大容器 }); diff --git a/types/layui/test/i18n.test.ts b/types/layui/test/i18n.test.ts new file mode 100644 index 00000000000000..989eef6ea6d061 --- /dev/null +++ b/types/layui/test/i18n.test.ts @@ -0,0 +1,30 @@ +function testI18n() { + const i18n = layui.i18n; + i18n.config; + i18n.set({ + locale: "zh-CN", + messages: { + "zh-CN": { + code: { + copy: "复制", + copied: "已复制", + }, + util: { + toDateString: { + meridiem(hours, minutes) { + return hours < 12 ? "AM" : "PM"; + }, + }, + }, + }, + }, + }); + + i18n.translate("code.copy"); + i18n.$t("util.toDateString.meridiem"); + i18n.$t("laydate.months"); + i18n.$t("laypage.total", { total: 10 }); + i18n.$t("laypage.total", { total: 10 }, { locale: "en-US", default: "Total: {total}" }); + // never + i18n.$t("custom"); +} diff --git a/types/layui/test/lay.test.ts b/types/layui/test/lay.test.ts index 2326929b118e69..1286ddb24e76be 100644 --- a/types/layui/test/lay.test.ts +++ b/types/layui/test/lay.test.ts @@ -2,6 +2,11 @@ function layTest() { const a = { a: [1, 2, 3] }; const b = {}; layui.lay.extend(b, a, { b: "ok" }); + layui.lay.extend(b, a, { a: [4, 5, 6] }, (objValue: any[], srcValue: any) => { + if (Array.isArray(objValue)) { + return objValue.concat(srcValue); + } + }); layui.lay.ie; layui.lay.layui; layui.lay.getPath; @@ -64,6 +69,44 @@ function layTest() { lay.onClickOutside(document.body, (e) => {}, { event: "pointerdown", scope: window, + capture: true, + detectIframe: true, + }); + lay.clipboard.writeText({ text: "123" }); + lay.isPlainObject({}); + lay.isPlainObject([]); + lay.isPlainObject(new Date()); + + lay.escape("123"); + lay.unescape("123"); + const resizeObserver = lay.createSharedResizeObserver("test"); + lay.removeEvent(window, "click", e => {}); + lay.removeEvent(document.body, "click", e => {}); + lay.addEvent(document, "click", e => {}, true); + lay.addEvent(document, "click", e => {}, { + capture: true, + }); + const flat1 = lay.treeToFlat([{ + id: 1, + children: [{ + id: 2, + }], + }]); + flat1[0].parentId; + flat1[1].children[0].id; + const flat2 = lay.treeToFlat([{ + id: 1, + children: [{ + id: 2, + }], + }], { + idKey: "id2", + parentKey: "parent2", + childrenKey: "children2", }); - lay.clipboard.writeText("123"); + flat2[0].parent2; + flat2[1].children2[0].id2; + const tree1 = lay.flatToTree(flat1); + tree1[0].id; + tree1[0].children[0].id; } diff --git a/types/layui/test/layer.test.ts b/types/layui/test/layer.test.ts index 8d09dc5b32c64e..581f1b7c0d6ab7 100644 --- a/types/layui/test/layer.test.ts +++ b/types/layui/test/layer.test.ts @@ -372,6 +372,12 @@ function layerTest() { }, ); + layui.layer.prompt( + { + formType: "email", + }, + ); + layer.tab({ area: ["600px", "300px"], tab: [ diff --git a/types/layui/test/nav.test.ts b/types/layui/test/nav.test.ts new file mode 100644 index 00000000000000..88475809284a0e --- /dev/null +++ b/types/layui/test/nav.test.ts @@ -0,0 +1,8 @@ +function navTest() { + layui.use("nav", () => { + const nav = layui.nav; + nav.render({ + elem: "#nav", + }); + }); +} diff --git a/types/layui/test/progress.test.ts b/types/layui/test/progress.test.ts new file mode 100644 index 00000000000000..2aea805393a136 --- /dev/null +++ b/types/layui/test/progress.test.ts @@ -0,0 +1,9 @@ +function progressTest() { + layui.use("progress", () => { + const progress = layui.progress; + progress.render({ + elem: "#progress1", + }); + progress.setValue("progress1", "50%"); + }); +} diff --git a/types/layui/test/slider.test.ts b/types/layui/test/slider.test.ts index af05a9a48c5f0e..d5c0c3a043f6bf 100644 --- a/types/layui/test/slider.test.ts +++ b/types/layui/test/slider.test.ts @@ -20,5 +20,8 @@ function SliderTest() { x.config.disabled; x.setValue(1); x.setValue(1, 0); + x.reload({ + value: [1, 2], + }); }); } diff --git a/types/layui/test/tab.test.ts b/types/layui/test/tab.test.ts new file mode 100644 index 00000000000000..b77a236afc9864 --- /dev/null +++ b/types/layui/test/tab.test.ts @@ -0,0 +1,19 @@ +function tabTest() { + layui.use("tab", () => { + const tab = layui.tab; + tab.render({ + elem: "#tab", + }); + tab.tabDelete("demo", "xxx"); // 删除 lay-id="xxx" 的这一项 + tab.tabAdd("demo", { + title: "选项卡的标题", + content: "

选项卡的内容
", // 支持传入html + id: "选项卡标题的lay-id属性值", + }); + tab.tabChange("demo", "layid"); + tab.tab({ + headerElem: "#tabHeader>li", // 指定tab头元素项 + bodyElem: "#tabBody>.xxx", // 指定tab主体元素项 + }); + }); +} diff --git a/types/layui/test/table.test.ts b/types/layui/test/table.test.ts index b376e675a840f0..b601327853ae62 100644 --- a/types/layui/test/table.test.ts +++ b/types/layui/test/table.test.ts @@ -105,6 +105,9 @@ function tableTest() { countName: "total", // 规定数据总数的字段名称,默认:count dataName: "rows", // 规定数据列表的字段名称,默认:data }, + syncFixedRowHeight: true, + ajax(options, type) { + }, }); rendered.config.cols; layui.use(["table", "laytpl", "element"], () => { diff --git a/types/layui/test/tree.test.ts b/types/layui/test/tree.test.ts index c04d0f11dac094..d48ad7fa8c24e2 100644 --- a/types/layui/test/tree.test.ts +++ b/types/layui/test/tree.test.ts @@ -89,10 +89,11 @@ function treeTest() { }, // 新的参数 }); - treeReloaded.config.abc; + layui.tree.on("click", () => { console.log(); }); layui.event("tree", "click", "params-a"); + tree.CONST; }); } diff --git a/types/layui/tsconfig.json b/types/layui/tsconfig.json index 24c292fb4b807b..21d3de13ab9858 100644 --- a/types/layui/tsconfig.json +++ b/types/layui/tsconfig.json @@ -16,22 +16,28 @@ "files": [ "index.d.ts", "layui-tests.ts", + "test/breadcrumb.test.ts", "test/carousel.test.ts", "test/code.test.ts", + "test/collapse.test.ts", "test/colorpicker.test.ts", "test/component.test.ts", "test/element.test.ts", "test/dropdown.test.ts", "test/flow.test.ts", "test/form.test.ts", + "test/i18n.test.ts", "test/jquery.test.ts", "test/lay.test.ts", "test/laydate.test.ts", "test/layedit.test.ts", "test/layer.test.ts", "test/laytpl.test.ts", + "test/nav.test.ts", + "test/progress.test.ts", "test/rate.test.ts", "test/slider.test.ts", + "test/tab.test.ts", "test/table.test.ts", "test/tabs.test.ts", "test/transfer.test.ts", diff --git a/types/node/net.d.ts b/types/node/net.d.ts index 9000542cae3ca2..a104853b1ad8d8 100644 --- a/types/node/net.d.ts +++ b/types/node/net.d.ts @@ -116,9 +116,14 @@ declare module "node:net" { * See `Writable` stream `write()` method for more * information. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. */ write(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean; + /** + * Sends data on the socket, with an explicit encoding for string data. + * @see {@link Socket.write} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + */ write(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean; /** * Initiate a connection on a given socket. @@ -358,12 +363,26 @@ declare module "node:net" { * * See `writable.end()` for further details. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. * @param callback Optional callback for when the socket is finished. * @return The socket itself. */ end(callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(buffer: Uint8Array | string, callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(str: Uint8Array | string, encoding?: BufferEncoding, callback?: () => void): this; // #region InternalEventEmitter addListener(eventName: E, listener: (...args: SocketEventMap[E]) => void): this; diff --git a/types/node/node-tests.ts b/types/node/node-tests.ts index 03b0e7f087b48c..06dcab94707cd3 100644 --- a/types/node/node-tests.ts +++ b/types/node/node-tests.ts @@ -30,6 +30,7 @@ import "./node-tests/repl"; import "./node-tests/sea"; import "./node-tests/sqlite"; import "./node-tests/stream"; +import "./node-tests/stream-web"; import "./node-tests/string_decoder"; import "./node-tests/test"; import "./node-tests/timers_promises"; diff --git a/types/node/node-tests/stream-web.ts b/types/node/node-tests/stream-web.ts new file mode 100644 index 00000000000000..044fd3c069a34a --- /dev/null +++ b/types/node/node-tests/stream-web.ts @@ -0,0 +1,103 @@ +import assert from "node:assert"; +import { ReadableStream, TransformStream, WritableStream } from "node:stream/web"; +import type { QueuingStrategySize } from "node:stream/web"; + +async function readResultHasRequiredValueProperty() { + const stream = ReadableStream.from(["test"]); + const reader = stream.getReader(); + { + const result = await reader.read(); + assert(!result.done, "Expected a non-done result"); + // $ExpectType string + result.value; + assert.strictEqual(result.value, "test"); + } + { + const result = await reader.read(); + assert(result.done, "Expected a done result"); + // $ExpectType string | undefined + result.value; + + assert.strictEqual(Object.prototype.hasOwnProperty.call(result, "value"), true); + assert.strictEqual(result.value, undefined); + } +} + +async function readableStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new ReadableStream({ + start(controller) { + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + controller.close(); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of stream) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function transformStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new TransformStream({ + transform(chunk, controller) { + assert.strictEqual(chunk, "input"); + + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of ReadableStream.from(["input"]).pipeThrough(stream)) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function writableStreamWriterTreatsExplicitUndefinedAsAChunk() { + const writtenChunks: (string | undefined)[] = []; + + const stream = new WritableStream({ + write(chunk) { + writtenChunks.push(chunk); + }, + }); + const writer = stream.getWriter(); + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + await writer.write(); + await writer.write(undefined); + await writer.close(); + + assert.deepStrictEqual(writtenChunks, [undefined, undefined]); +} + +async function queuingStrategySizeReceivesTheChunk() { + const sizeChunks: string[] = []; + + const size: QueuingStrategySize = (chunk) => { + // $ExpectType string + chunk; + sizeChunks.push(chunk); + return chunk.length; + }; + + const stream = new ReadableStream( + { + start(controller) { + controller.enqueue("size"); + controller.close(); + }, + }, + { + highWaterMark: 10, + size, + }, + ); + const result = await stream.getReader().read(); + assert.deepStrictEqual(result, { done: false, value: "size" }); + assert.deepStrictEqual(sizeChunks, ["size"]); +} diff --git a/types/node/stream.d.ts b/types/node/stream.d.ts index 3f046ac34f7e07..f625e711ec37c3 100644 --- a/types/node/stream.d.ts +++ b/types/node/stream.d.ts @@ -899,11 +899,20 @@ declare module "node:stream" { * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param [encoding='utf8'] The encoding, if `chunk` is a string. * @param callback Callback for when this chunk of data is flushed. * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. */ write(chunk: any, callback?: (error: Error | null | undefined) => void): boolean; + /** + * Writes data to the stream, with an explicit encoding for string data. + * @see {@link Writable.write} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding, if `chunk` is a string. + * @param callback Callback for when this chunk of data is flushed. + * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ write(chunk: any, encoding: BufferEncoding, callback?: (error: Error | null | undefined) => void): boolean; /** * The `writable.setDefaultEncoding()` method sets the default `encoding` for a `Writable` stream. @@ -928,13 +937,27 @@ declare module "node:stream" { * // Writing more now is not allowed! * ``` * @since v0.9.4 + * @param cb Callback for when the stream is finished. + */ + end(cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param encoding The encoding if `chunk` is a string - * @param callback Callback for when the stream is finished. + * @param cb Callback for when the stream is finished. */ - end(cb?: () => void): this; end(chunk: any, cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding if `chunk` is a string + * @param cb Callback for when the stream is finished. + */ end(chunk: any, encoding: BufferEncoding, cb?: () => void): this; /** * The `writable.cork()` method forces all written data to be buffered in memory. diff --git a/types/node/stream/web.d.ts b/types/node/stream/web.d.ts index 32ce4069aa01d2..13d55a0626ba25 100644 --- a/types/node/stream/web.d.ts +++ b/types/node/stream/web.d.ts @@ -206,7 +206,7 @@ declare module "node:stream/web" { interface ReadableStreamDefaultController { readonly desiredSize: number | null; close(): void; - enqueue(chunk?: R): void; + enqueue(chunk: R): void; error(e?: any): void; } var ReadableStreamDefaultController: { @@ -251,7 +251,7 @@ declare module "node:stream/web" { }; interface TransformStreamDefaultController { readonly desiredSize: number | null; - enqueue(chunk?: O): void; + enqueue(chunk: O): void; error(reason?: any): void; terminate(): void; } @@ -284,7 +284,7 @@ declare module "node:stream/web" { abort(reason?: any): Promise; close(): Promise; releaseLock(): void; - write(chunk?: W): Promise; + write(chunk: W): Promise; } var WritableStreamDefaultWriter: { prototype: WritableStreamDefaultWriter; diff --git a/types/node/v20/net.d.ts b/types/node/v20/net.d.ts index 06894725445018..44297f66cf9441 100644 --- a/types/node/v20/net.d.ts +++ b/types/node/v20/net.d.ts @@ -111,9 +111,14 @@ declare module "net" { * See `Writable` stream `write()` method for more * information. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. */ write(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean; + /** + * Sends data on the socket, with an explicit encoding for string data. + * @see {@link Socket.write} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + */ write(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean; /** * Initiate a connection on a given socket. @@ -353,12 +358,26 @@ declare module "net" { * * See `writable.end()` for further details. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. * @param callback Optional callback for when the socket is finished. * @return The socket itself. */ end(callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(buffer: Uint8Array | string, callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(str: Uint8Array | string, encoding?: BufferEncoding, callback?: () => void): this; /** * events.EventEmitter diff --git a/types/node/v20/node-tests.ts b/types/node/v20/node-tests.ts index ad1a8922092b9f..148d5d7f493567 100644 --- a/types/node/v20/node-tests.ts +++ b/types/node/v20/node-tests.ts @@ -28,6 +28,7 @@ import "./test/readline"; import "./test/repl"; import "./test/sea"; import "./test/stream"; +import "./test/stream-web"; import "./test/string_decoder"; import "./test/test"; import "./test/timers_promises"; diff --git a/types/node/v20/stream.d.ts b/types/node/v20/stream.d.ts index 9d13d1bb629948..1e0eb8faceacb3 100644 --- a/types/node/v20/stream.d.ts +++ b/types/node/v20/stream.d.ts @@ -865,11 +865,20 @@ declare module "stream" { * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param [encoding='utf8'] The encoding, if `chunk` is a string. * @param callback Callback for when this chunk of data is flushed. * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. */ write(chunk: any, callback?: (error: Error | null | undefined) => void): boolean; + /** + * Writes data to the stream, with an explicit encoding for string data. + * @see {@link Writable.write} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding, if `chunk` is a string. + * @param callback Callback for when this chunk of data is flushed. + * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ write(chunk: any, encoding: BufferEncoding, callback?: (error: Error | null | undefined) => void): boolean; /** * The `writable.setDefaultEncoding()` method sets the default `encoding` for a `Writable` stream. @@ -894,13 +903,27 @@ declare module "stream" { * // Writing more now is not allowed! * ``` * @since v0.9.4 + * @param cb Callback for when the stream is finished. + */ + end(cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param encoding The encoding if `chunk` is a string - * @param callback Callback for when the stream is finished. + * @param cb Callback for when the stream is finished. */ - end(cb?: () => void): this; end(chunk: any, cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding if `chunk` is a string + * @param cb Callback for when the stream is finished. + */ end(chunk: any, encoding: BufferEncoding, cb?: () => void): this; /** * The `writable.cork()` method forces all written data to be buffered in memory. diff --git a/types/node/v20/stream/web.d.ts b/types/node/v20/stream/web.d.ts index 039ddc9ae3604d..6c631ecd838de9 100644 --- a/types/node/v20/stream/web.d.ts +++ b/types/node/v20/stream/web.d.ts @@ -109,7 +109,7 @@ declare module "stream/web" { } interface ReadableStreamReadDoneResult { done: true; - value?: T; + value: T | undefined; } type ReadableStreamReadResult = ReadableStreamReadValueResult | ReadableStreamReadDoneResult; interface ReadableByteStreamControllerCallback { @@ -251,7 +251,7 @@ declare module "stream/web" { interface ReadableStreamDefaultController { readonly desiredSize: number | null; close(): void; - enqueue(chunk?: R): void; + enqueue(chunk: R): void; error(e?: any): void; } const ReadableStreamDefaultController: { @@ -279,7 +279,7 @@ declare module "stream/web" { }; interface TransformStreamDefaultController { readonly desiredSize: number | null; - enqueue(chunk?: O): void; + enqueue(chunk: O): void; error(reason?: any): void; terminate(): void; } @@ -315,7 +315,7 @@ declare module "stream/web" { abort(reason?: any): Promise; close(): Promise; releaseLock(): void; - write(chunk?: W): Promise; + write(chunk: W): Promise; } const WritableStreamDefaultWriter: { prototype: WritableStreamDefaultWriter; @@ -339,7 +339,7 @@ declare module "stream/web" { size?: QueuingStrategySize; } interface QueuingStrategySize { - (chunk?: T): number; + (chunk: T): number; } interface QueuingStrategyInit { /** diff --git a/types/node/v20/test/stream-web.ts b/types/node/v20/test/stream-web.ts new file mode 100644 index 00000000000000..044fd3c069a34a --- /dev/null +++ b/types/node/v20/test/stream-web.ts @@ -0,0 +1,103 @@ +import assert from "node:assert"; +import { ReadableStream, TransformStream, WritableStream } from "node:stream/web"; +import type { QueuingStrategySize } from "node:stream/web"; + +async function readResultHasRequiredValueProperty() { + const stream = ReadableStream.from(["test"]); + const reader = stream.getReader(); + { + const result = await reader.read(); + assert(!result.done, "Expected a non-done result"); + // $ExpectType string + result.value; + assert.strictEqual(result.value, "test"); + } + { + const result = await reader.read(); + assert(result.done, "Expected a done result"); + // $ExpectType string | undefined + result.value; + + assert.strictEqual(Object.prototype.hasOwnProperty.call(result, "value"), true); + assert.strictEqual(result.value, undefined); + } +} + +async function readableStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new ReadableStream({ + start(controller) { + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + controller.close(); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of stream) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function transformStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new TransformStream({ + transform(chunk, controller) { + assert.strictEqual(chunk, "input"); + + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of ReadableStream.from(["input"]).pipeThrough(stream)) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function writableStreamWriterTreatsExplicitUndefinedAsAChunk() { + const writtenChunks: (string | undefined)[] = []; + + const stream = new WritableStream({ + write(chunk) { + writtenChunks.push(chunk); + }, + }); + const writer = stream.getWriter(); + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + await writer.write(); + await writer.write(undefined); + await writer.close(); + + assert.deepStrictEqual(writtenChunks, [undefined, undefined]); +} + +async function queuingStrategySizeReceivesTheChunk() { + const sizeChunks: string[] = []; + + const size: QueuingStrategySize = (chunk) => { + // $ExpectType string + chunk; + sizeChunks.push(chunk); + return chunk.length; + }; + + const stream = new ReadableStream( + { + start(controller) { + controller.enqueue("size"); + controller.close(); + }, + }, + { + highWaterMark: 10, + size, + }, + ); + const result = await stream.getReader().read(); + assert.deepStrictEqual(result, { done: false, value: "size" }); + assert.deepStrictEqual(sizeChunks, ["size"]); +} diff --git a/types/node/v22/net.d.ts b/types/node/v22/net.d.ts index d29b929a0d179f..41cc319493b21d 100644 --- a/types/node/v22/net.d.ts +++ b/types/node/v22/net.d.ts @@ -108,9 +108,14 @@ declare module "net" { * See `Writable` stream `write()` method for more * information. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. */ write(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean; + /** + * Sends data on the socket, with an explicit encoding for string data. + * @see {@link Socket.write} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + */ write(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean; /** * Initiate a connection on a given socket. @@ -350,12 +355,26 @@ declare module "net" { * * See `writable.end()` for further details. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. * @param callback Optional callback for when the socket is finished. * @return The socket itself. */ end(callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(buffer: Uint8Array | string, callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(str: Uint8Array | string, encoding?: BufferEncoding, callback?: () => void): this; /** * events.EventEmitter diff --git a/types/node/v22/node-tests.ts b/types/node/v22/node-tests.ts index 0d58fb3f6c7b3d..04b9b8deacf057 100644 --- a/types/node/v22/node-tests.ts +++ b/types/node/v22/node-tests.ts @@ -29,6 +29,7 @@ import "./test/repl"; import "./test/sea"; import "./test/sqlite"; import "./test/stream"; +import "./test/stream-web"; import "./test/string_decoder"; import "./test/test"; import "./test/timers_promises"; diff --git a/types/node/v22/stream.d.ts b/types/node/v22/stream.d.ts index 5799cc0305f675..dec1bcc2171166 100644 --- a/types/node/v22/stream.d.ts +++ b/types/node/v22/stream.d.ts @@ -858,11 +858,20 @@ declare module "stream" { * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param [encoding='utf8'] The encoding, if `chunk` is a string. * @param callback Callback for when this chunk of data is flushed. * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. */ write(chunk: any, callback?: (error: Error | null | undefined) => void): boolean; + /** + * Writes data to the stream, with an explicit encoding for string data. + * @see {@link Writable.write} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding, if `chunk` is a string. + * @param callback Callback for when this chunk of data is flushed. + * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ write(chunk: any, encoding: BufferEncoding, callback?: (error: Error | null | undefined) => void): boolean; /** * The `writable.setDefaultEncoding()` method sets the default `encoding` for a `Writable` stream. @@ -887,13 +896,27 @@ declare module "stream" { * // Writing more now is not allowed! * ``` * @since v0.9.4 + * @param cb Callback for when the stream is finished. + */ + end(cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param encoding The encoding if `chunk` is a string - * @param callback Callback for when the stream is finished. + * @param cb Callback for when the stream is finished. */ - end(cb?: () => void): this; end(chunk: any, cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding if `chunk` is a string + * @param cb Callback for when the stream is finished. + */ end(chunk: any, encoding: BufferEncoding, cb?: () => void): this; /** * The `writable.cork()` method forces all written data to be buffered in memory. diff --git a/types/node/v22/stream/web.d.ts b/types/node/v22/stream/web.d.ts index 8d348a398cd250..c7432f8bbe120c 100644 --- a/types/node/v22/stream/web.d.ts +++ b/types/node/v22/stream/web.d.ts @@ -109,7 +109,7 @@ declare module "stream/web" { } interface ReadableStreamReadDoneResult { done: true; - value?: T; + value: T | undefined; } type ReadableStreamReadResult = ReadableStreamReadValueResult | ReadableStreamReadDoneResult; interface ReadableByteStreamControllerCallback { @@ -254,7 +254,7 @@ declare module "stream/web" { interface ReadableStreamDefaultController { readonly desiredSize: number | null; close(): void; - enqueue(chunk?: R): void; + enqueue(chunk: R): void; error(e?: any): void; } const ReadableStreamDefaultController: { @@ -283,7 +283,7 @@ declare module "stream/web" { }; interface TransformStreamDefaultController { readonly desiredSize: number | null; - enqueue(chunk?: O): void; + enqueue(chunk: O): void; error(reason?: any): void; terminate(): void; } @@ -319,7 +319,7 @@ declare module "stream/web" { abort(reason?: any): Promise; close(): Promise; releaseLock(): void; - write(chunk?: W): Promise; + write(chunk: W): Promise; } const WritableStreamDefaultWriter: { prototype: WritableStreamDefaultWriter; @@ -343,7 +343,7 @@ declare module "stream/web" { size?: QueuingStrategySize; } interface QueuingStrategySize { - (chunk?: T): number; + (chunk: T): number; } interface QueuingStrategyInit { /** diff --git a/types/node/v22/test/stream-web.ts b/types/node/v22/test/stream-web.ts new file mode 100644 index 00000000000000..044fd3c069a34a --- /dev/null +++ b/types/node/v22/test/stream-web.ts @@ -0,0 +1,103 @@ +import assert from "node:assert"; +import { ReadableStream, TransformStream, WritableStream } from "node:stream/web"; +import type { QueuingStrategySize } from "node:stream/web"; + +async function readResultHasRequiredValueProperty() { + const stream = ReadableStream.from(["test"]); + const reader = stream.getReader(); + { + const result = await reader.read(); + assert(!result.done, "Expected a non-done result"); + // $ExpectType string + result.value; + assert.strictEqual(result.value, "test"); + } + { + const result = await reader.read(); + assert(result.done, "Expected a done result"); + // $ExpectType string | undefined + result.value; + + assert.strictEqual(Object.prototype.hasOwnProperty.call(result, "value"), true); + assert.strictEqual(result.value, undefined); + } +} + +async function readableStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new ReadableStream({ + start(controller) { + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + controller.close(); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of stream) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function transformStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new TransformStream({ + transform(chunk, controller) { + assert.strictEqual(chunk, "input"); + + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of ReadableStream.from(["input"]).pipeThrough(stream)) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function writableStreamWriterTreatsExplicitUndefinedAsAChunk() { + const writtenChunks: (string | undefined)[] = []; + + const stream = new WritableStream({ + write(chunk) { + writtenChunks.push(chunk); + }, + }); + const writer = stream.getWriter(); + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + await writer.write(); + await writer.write(undefined); + await writer.close(); + + assert.deepStrictEqual(writtenChunks, [undefined, undefined]); +} + +async function queuingStrategySizeReceivesTheChunk() { + const sizeChunks: string[] = []; + + const size: QueuingStrategySize = (chunk) => { + // $ExpectType string + chunk; + sizeChunks.push(chunk); + return chunk.length; + }; + + const stream = new ReadableStream( + { + start(controller) { + controller.enqueue("size"); + controller.close(); + }, + }, + { + highWaterMark: 10, + size, + }, + ); + const result = await stream.getReader().read(); + assert.deepStrictEqual(result, { done: false, value: "size" }); + assert.deepStrictEqual(sizeChunks, ["size"]); +} diff --git a/types/node/v24/net.d.ts b/types/node/v24/net.d.ts index 38c1627505dba0..7dac21f328982f 100644 --- a/types/node/v24/net.d.ts +++ b/types/node/v24/net.d.ts @@ -105,9 +105,14 @@ declare module "net" { * See `Writable` stream `write()` method for more * information. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. */ write(buffer: Uint8Array | string, cb?: (err?: Error | null) => void): boolean; + /** + * Sends data on the socket, with an explicit encoding for string data. + * @see {@link Socket.write} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + */ write(str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error | null) => void): boolean; /** * Initiate a connection on a given socket. @@ -347,12 +352,26 @@ declare module "net" { * * See `writable.end()` for further details. * @since v0.1.90 - * @param [encoding='utf8'] Only used when data is `string`. * @param callback Optional callback for when the socket is finished. * @return The socket itself. */ end(callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(buffer: Uint8Array | string, callback?: () => void): this; + /** + * Half-closes the socket, with one final chunk of data. + * @see {@link Socket.end} for full details. + * @since v0.1.90 + * @param [encoding='utf8'] Only used when data is `string`. + * @param callback Optional callback for when the socket is finished. + * @return The socket itself. + */ end(str: Uint8Array | string, encoding?: BufferEncoding, callback?: () => void): this; /** * events.EventEmitter diff --git a/types/node/v24/node-tests.ts b/types/node/v24/node-tests.ts index 0d58fb3f6c7b3d..04b9b8deacf057 100644 --- a/types/node/v24/node-tests.ts +++ b/types/node/v24/node-tests.ts @@ -29,6 +29,7 @@ import "./test/repl"; import "./test/sea"; import "./test/sqlite"; import "./test/stream"; +import "./test/stream-web"; import "./test/string_decoder"; import "./test/test"; import "./test/timers_promises"; diff --git a/types/node/v24/stream.d.ts b/types/node/v24/stream.d.ts index 3b38302b9adfa0..0aec9a2ae97ba4 100644 --- a/types/node/v24/stream.d.ts +++ b/types/node/v24/stream.d.ts @@ -863,11 +863,20 @@ declare module "stream" { * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param [encoding='utf8'] The encoding, if `chunk` is a string. * @param callback Callback for when this chunk of data is flushed. * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. */ write(chunk: any, callback?: (error: Error | null | undefined) => void): boolean; + /** + * Writes data to the stream, with an explicit encoding for string data. + * @see {@link Writable.write} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding, if `chunk` is a string. + * @param callback Callback for when this chunk of data is flushed. + * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ write(chunk: any, encoding: BufferEncoding, callback?: (error: Error | null | undefined) => void): boolean; /** * The `writable.setDefaultEncoding()` method sets the default `encoding` for a `Writable` stream. @@ -892,13 +901,27 @@ declare module "stream" { * // Writing more now is not allowed! * ``` * @since v0.9.4 + * @param cb Callback for when the stream is finished. + */ + end(cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. - * @param encoding The encoding if `chunk` is a string - * @param callback Callback for when the stream is finished. + * @param cb Callback for when the stream is finished. */ - end(cb?: () => void): this; end(chunk: any, cb?: () => void): this; + /** + * Signals that no more data will be written, with one final chunk of data. + * @see {@link Writable.end} for full details. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a {string}, {Buffer}, + * {TypedArray} or {DataView}. For object mode streams, `chunk` may be any JavaScript value other than `null`. + * @param encoding The encoding if `chunk` is a string + * @param cb Callback for when the stream is finished. + */ end(chunk: any, encoding: BufferEncoding, cb?: () => void): this; /** * The `writable.cork()` method forces all written data to be buffered in memory. diff --git a/types/node/v24/stream/web.d.ts b/types/node/v24/stream/web.d.ts index 136886052666e0..fc88e13fff057b 100644 --- a/types/node/v24/stream/web.d.ts +++ b/types/node/v24/stream/web.d.ts @@ -105,7 +105,7 @@ declare module "stream/web" { } interface ReadableStreamReadDoneResult { done: true; - value?: T; + value: T | undefined; } type ReadableStreamReadResult = ReadableStreamReadValueResult | ReadableStreamReadDoneResult; interface ReadableByteStreamControllerCallback { @@ -250,7 +250,7 @@ declare module "stream/web" { interface ReadableStreamDefaultController { readonly desiredSize: number | null; close(): void; - enqueue(chunk?: R): void; + enqueue(chunk: R): void; error(e?: any): void; } const ReadableStreamDefaultController: { @@ -279,7 +279,7 @@ declare module "stream/web" { }; interface TransformStreamDefaultController { readonly desiredSize: number | null; - enqueue(chunk?: O): void; + enqueue(chunk: O): void; error(reason?: any): void; terminate(): void; } @@ -315,7 +315,7 @@ declare module "stream/web" { abort(reason?: any): Promise; close(): Promise; releaseLock(): void; - write(chunk?: W): Promise; + write(chunk: W): Promise; } const WritableStreamDefaultWriter: { prototype: WritableStreamDefaultWriter; @@ -339,7 +339,7 @@ declare module "stream/web" { size?: QueuingStrategySize; } interface QueuingStrategySize { - (chunk?: T): number; + (chunk: T): number; } interface QueuingStrategyInit { /** diff --git a/types/node/v24/test/stream-web.ts b/types/node/v24/test/stream-web.ts new file mode 100644 index 00000000000000..044fd3c069a34a --- /dev/null +++ b/types/node/v24/test/stream-web.ts @@ -0,0 +1,103 @@ +import assert from "node:assert"; +import { ReadableStream, TransformStream, WritableStream } from "node:stream/web"; +import type { QueuingStrategySize } from "node:stream/web"; + +async function readResultHasRequiredValueProperty() { + const stream = ReadableStream.from(["test"]); + const reader = stream.getReader(); + { + const result = await reader.read(); + assert(!result.done, "Expected a non-done result"); + // $ExpectType string + result.value; + assert.strictEqual(result.value, "test"); + } + { + const result = await reader.read(); + assert(result.done, "Expected a done result"); + // $ExpectType string | undefined + result.value; + + assert.strictEqual(Object.prototype.hasOwnProperty.call(result, "value"), true); + assert.strictEqual(result.value, undefined); + } +} + +async function readableStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new ReadableStream({ + start(controller) { + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + controller.close(); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of stream) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function transformStreamControllerTreatsExplicitUndefinedAsAChunk() { + const stream = new TransformStream({ + transform(chunk, controller) { + assert.strictEqual(chunk, "input"); + + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + controller.enqueue(); + controller.enqueue(undefined); + }, + }); + const values: (string | undefined)[] = []; + for await (const chunk of ReadableStream.from(["input"]).pipeThrough(stream)) { + values.push(chunk); + } + // enqueue() and enqueue(undefined) should have the same effect + assert.deepStrictEqual(values, [undefined, undefined]); +} + +async function writableStreamWriterTreatsExplicitUndefinedAsAChunk() { + const writtenChunks: (string | undefined)[] = []; + + const stream = new WritableStream({ + write(chunk) { + writtenChunks.push(chunk); + }, + }); + const writer = stream.getWriter(); + // @ts-expect-error `chunk` is required; pass `undefined` explicitly when it is part of the chunk type. + await writer.write(); + await writer.write(undefined); + await writer.close(); + + assert.deepStrictEqual(writtenChunks, [undefined, undefined]); +} + +async function queuingStrategySizeReceivesTheChunk() { + const sizeChunks: string[] = []; + + const size: QueuingStrategySize = (chunk) => { + // $ExpectType string + chunk; + sizeChunks.push(chunk); + return chunk.length; + }; + + const stream = new ReadableStream( + { + start(controller) { + controller.enqueue("size"); + controller.close(); + }, + }, + { + highWaterMark: 10, + size, + }, + ); + const result = await stream.getReader().read(); + assert.deepStrictEqual(result, { done: false, value: "size" }); + assert.deepStrictEqual(sizeChunks, ["size"]); +} diff --git a/types/smtp-server/index.d.ts b/types/smtp-server/index.d.ts index 37980bd69ccc1f..e4bc26ef912bff 100644 --- a/types/smtp-server/index.d.ts +++ b/types/smtp-server/index.d.ts @@ -209,6 +209,10 @@ export interface SMTPServerOptions extends tls.TlsOptions { * Enhanced status codes are disabled by default. */ hideENHANCEDSTATUSCODES?: boolean | undefined; + /** + * optional boolean, if set to true then does not show DSN in features list, by default DSN is disabled + */ + hideDSN?: boolean; /** * optional boolean, if set to true allows authentication even if connection is not secured first */ @@ -295,7 +299,11 @@ export interface SMTPServerOptions extends tls.TlsOptions { /** * the callback to handle incoming messages ([see details](https://nodemailer.com/extras/smtp-server#processing-incoming-messages-ondata)) */ - onData?(stream: SMTPServerDataStream, session: SMTPServerSession, callback: (err?: Error | null) => void): void; + onData?( + stream: SMTPServerDataStream, + session: SMTPServerSession, + callback: (err?: Error | null, message?: string) => void, + ): void; /** * the callback that informs about closed client connection */ @@ -339,7 +347,11 @@ export class SMTPServer extends EventEmitter { /** Override this */ onConnect(session: SMTPServerSession, callback: (err?: Error | null) => void): void; /** Override this */ - onData(stream: SMTPServerDataStream, session: SMTPServerSession, callback: (err?: Error | null) => void): void; + onData( + stream: SMTPServerDataStream, + session: SMTPServerSession, + callback: (err?: Error | null, message?: string) => void, + ): void; /** Override this */ onMailFrom(address: SMTPServerAddress, session: SMTPServerSession, callback: (err?: Error | null) => void): void; /** Override this */ diff --git a/types/smtp-server/smtp-server-tests.ts b/types/smtp-server/smtp-server-tests.ts index cd7c14ebb6512e..2836a8fdd1423b 100644 --- a/types/smtp-server/smtp-server-tests.ts +++ b/types/smtp-server/smtp-server-tests.ts @@ -190,3 +190,17 @@ function test_newly_added_options() { }, }); } + +function test_hideDSN_option() { + const smtpServerInstance = new SMTPServer({ + hideDSN: true, + }); +} + +function test_onData_with_message() { + const smtpServerInstance = new SMTPServer({ + onData(stream, session, callback) { + callback(null, "Message queued"); + }, + }); +} diff --git a/types/textile-js/.npmignore b/types/textile-js/.npmignore new file mode 100644 index 00000000000000..93e307400a5456 --- /dev/null +++ b/types/textile-js/.npmignore @@ -0,0 +1,5 @@ +* +!**/*.d.ts +!**/*.d.cts +!**/*.d.mts +!**/*.d.*.ts diff --git a/types/textile-js/index.d.ts b/types/textile-js/index.d.ts new file mode 100644 index 00000000000000..a159d66d1a9204 --- /dev/null +++ b/types/textile-js/index.d.ts @@ -0,0 +1,35 @@ +export = textile; +declare function textile(txt: string, opts?: textile.Options): string; +declare namespace textile { + type Tag = string; + type JMLAttributes = Record; + type JMLElement = [Tag, ...(JMLAttributes | JMLElement | string)[]]; + type JMLNode = Array & { sourceLength?: number }; + type JMLDocument = ["html", ...(JMLElement | string)[]]; + type TokenType = "OPEN" | "CLOSE" | "TEXT" | "SINGLE" | "WS" | "COMMENT"; + type Token = + | { + type: "OPEN" | "CLOSE" | "SINGLE"; + tag: Tag; + attr?: JMLAttributes; + pos: number; + src: string; + } + | { + type: "TEXT" | "WS" | "COMMENT"; + data: string; + pos: number; + src: string; + }; + interface Options { + breaks?: boolean; + } + const defaults: Options; + function setOptions(opt: Options): typeof textile; + function setoptions(opt: Options): typeof textile; + function parse(txt: string, opts?: Options): string; + function convert(txt: string, opts?: Options): string; + function jsonml(txt: string, opts?: Options): JMLDocument; + function serialize(jsonml: JMLElement | string): string; + function html_parser(tokens: Token[], lazy?: boolean): JMLNode; +} diff --git a/types/textile-js/package.json b/types/textile-js/package.json new file mode 100644 index 00000000000000..a449a0683d9679 --- /dev/null +++ b/types/textile-js/package.json @@ -0,0 +1,17 @@ +{ + "private": true, + "name": "@types/textile-js", + "version": "2.1.9999", + "projects": [ + "https://github.com/borgar/textile-js" + ], + "devDependencies": { + "@types/textile-js": "workspace:." + }, + "owners": [ + { + "name": "phothinmg", + "githubUsername": "phothinmg" + } + ] +} diff --git a/types/textile-js/textile-js-tests.ts b/types/textile-js/textile-js-tests.ts new file mode 100644 index 00000000000000..9154a02b037e3f --- /dev/null +++ b/types/textile-js/textile-js-tests.ts @@ -0,0 +1,39 @@ +import textile = require("textile-js"); + +const txt = "h1. Hello World"; +const opt: textile.Options = { breaks: true }; + +const rendered: string = textile(txt, opt); + +const parsed: string = textile.parse(txt, opt); +const converted: string = textile.convert(txt, opt); + +const doc: textile.JMLDocument = textile.jsonml(txt, opt); +const serializedDoc: string = textile.serialize(doc); + +const node: textile.JMLElement = ["p", { class: "intro", "data-id": 1 }, "Hello"]; +const serializedNode: string = textile.serialize(node); + +const textOnlySerialized: string = textile.serialize("plain text"); + +const defaults: textile.Options = textile.defaults; +const chained: typeof textile = textile.setOptions(defaults).setoptions({ breaks: false }); + +const tokens: textile.Token[] = [ + { type: "OPEN", tag: "p", pos: 0, src: "

" }, + { type: "TEXT", data: "Hello", pos: 3, src: "Hello" }, + { type: "CLOSE", tag: "p", pos: 8, src: "

" }, +]; + +const parsedNode: textile.JMLNode = textile.html_parser(tokens); +const parsedNodeLazy: textile.JMLNode = textile.html_parser(tokens, true); + +void rendered; +void parsed; +void converted; +void serializedDoc; +void serializedNode; +void textOnlySerialized; +void chained; +void parsedNode; +void parsedNodeLazy; diff --git a/types/textile-js/tsconfig.json b/types/textile-js/tsconfig.json new file mode 100644 index 00000000000000..93581b17e518b4 --- /dev/null +++ b/types/textile-js/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "node16", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "textile-js-tests.ts" + ] +}