diff --git a/packages/vite/package.json b/packages/vite/package.json index 296afe90152d6b..6a0c0999ab9cfa 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -54,6 +54,8 @@ }, "devDependencies": { "@ampproject/remapping": "^1.0.1", + "@babel/parser": "^7.15.8", + "@babel/types": "^7.15.6", "@rollup/plugin-alias": "^3.1.5", "@rollup/plugin-commonjs": "^21.0.0", "@rollup/plugin-dynamic-import-vars": "^1.4.0", diff --git a/packages/vite/src/node/index.ts b/packages/vite/src/node/index.ts index 6fb83f05174c31..2835c658e013b0 100644 --- a/packages/vite/src/node/index.ts +++ b/packages/vite/src/node/index.ts @@ -81,6 +81,7 @@ export type { ErrorPayload } from 'types/hmrPayload' export type { Connect } from 'types/connect' +export type { WebSocket } from 'types/ws' export type { HttpProxy } from 'types/http-proxy' export type { FSWatcher, WatchOptions } from 'types/chokidar' export type { Terser } from 'types/terser' diff --git a/packages/vite/src/node/server/ws.ts b/packages/vite/src/node/server/ws.ts index 499da9a0c6668e..82d9cb01e09642 100644 --- a/packages/vite/src/node/server/ws.ts +++ b/packages/vite/src/node/server/ws.ts @@ -5,6 +5,7 @@ import { ServerOptions as HttpsServerOptions } from 'https' import WebSocket from 'ws' +import { WebSocket as WebSocketTypes } from 'types/ws' import { ErrorPayload, HMRPayload } from 'types/hmrPayload' import { ResolvedConfig } from '..' import { isObject } from '../utils' @@ -12,8 +13,8 @@ import { Socket } from 'net' export const HMR_HEADER = 'vite-hmr' export interface WebSocketServer { - on: WebSocket.Server['on'] - off: WebSocket.Server['off'] + on: WebSocketTypes.Server['on'] + off: WebSocketTypes.Server['off'] send(payload: HMRPayload): void close(): Promise } diff --git a/packages/vite/types/ws.d.ts b/packages/vite/types/ws.d.ts new file mode 100644 index 00000000000000..3a174f9df47ea7 --- /dev/null +++ b/packages/vite/types/ws.d.ts @@ -0,0 +1,532 @@ +// Inlined to avoid extra dependency +// MIT Licensed https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/LICENSE + +// Type definitions for ws 7.4 +// Project: https://github.com/websockets/ws +// Definitions by: Paul Loyd +// Margus Lamp +// Philippe D'Alva +// reduckted +// teidesu +// Bartosz Wojtkowiak +// Kyle Hensel +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/// + +import { EventEmitter } from 'events' +import { + Agent, + ClientRequest, + ClientRequestArgs, + IncomingMessage, + OutgoingHttpHeaders, + Server as HTTPServer +} from 'http' +import { Server as HTTPSServer } from 'https' +import { Socket } from 'net' +import { Duplex, DuplexOptions } from 'stream' +import { SecureContextOptions } from 'tls' +import { URL } from 'url' +import { ZlibOptions } from 'zlib' + +export declare namespace WebSocket { + // WebSocket socket. + export class WebSocket extends EventEmitter { + /** The connection is not yet open. */ + static readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + static readonly OPEN: 1 + /** The connection is in the process of closing. */ + static readonly CLOSING: 2 + /** The connection is closed. */ + static readonly CLOSED: 3 + + binaryType: 'nodebuffer' | 'arraybuffer' | 'fragments' + readonly bufferedAmount: number + readonly extensions: string + readonly protocol: string + /** The current state of the connection */ + readonly readyState: + | typeof WebSocket.CONNECTING + | typeof WebSocket.OPEN + | typeof WebSocket.CLOSING + | typeof WebSocket.CLOSED + readonly url: string + + /** The connection is not yet open. */ + readonly CONNECTING: 0 + /** The connection is open and ready to communicate. */ + readonly OPEN: 1 + /** The connection is in the process of closing. */ + readonly CLOSING: 2 + /** The connection is closed. */ + readonly CLOSED: 3 + + onopen: (event: WebSocket.OpenEvent) => void + onerror: (event: WebSocket.ErrorEvent) => void + onclose: (event: WebSocket.CloseEvent) => void + onmessage: (event: WebSocket.MessageEvent) => void + + constructor( + address: string | URL, + options?: WebSocket.ClientOptions | ClientRequestArgs + ) + constructor( + address: string | URL, + protocols?: string | string[], + options?: WebSocket.ClientOptions | ClientRequestArgs + ) + + close(code?: number, data?: string): void + ping(data?: any, mask?: boolean, cb?: (err: Error) => void): void + pong(data?: any, mask?: boolean, cb?: (err: Error) => void): void + send(data: any, cb?: (err?: Error) => void): void + send( + data: any, + options: { + mask?: boolean | undefined + binary?: boolean | undefined + compress?: boolean | undefined + fin?: boolean | undefined + }, + cb?: (err?: Error) => void + ): void + terminate(): void + + // HTML5 WebSocket events + addEventListener( + method: 'message', + cb: (event: { data: any; type: string; target: WebSocket }) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: 'close', + cb: (event: { + wasClean: boolean + code: number + reason: string + target: WebSocket + }) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: 'error', + cb: (event: { + error: any + message: any + type: string + target: WebSocket + }) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: 'open', + cb: (event: { target: WebSocket }) => void, + options?: WebSocket.EventListenerOptions + ): void + addEventListener( + method: string, + listener: () => void, + options?: WebSocket.EventListenerOptions + ): void + + removeEventListener( + method: 'message', + cb?: (event: { data: any; type: string; target: WebSocket }) => void + ): void + removeEventListener( + method: 'close', + cb?: (event: { + wasClean: boolean + code: number + reason: string + target: WebSocket + }) => void + ): void + removeEventListener( + method: 'error', + cb?: (event: { + error: any + message: any + type: string + target: WebSocket + }) => void + ): void + removeEventListener( + method: 'open', + cb?: (event: { target: WebSocket }) => void + ): void + removeEventListener(method: string, listener?: () => void): void + + // Events + on( + event: 'close', + listener: (this: WebSocket, code: number, reason: string) => void + ): this + on(event: 'error', listener: (this: WebSocket, err: Error) => void): this + on( + event: 'upgrade', + listener: (this: WebSocket, request: IncomingMessage) => void + ): this + on( + event: 'message', + listener: (this: WebSocket, data: WebSocket.Data) => void + ): this + on(event: 'open', listener: (this: WebSocket) => void): this + on( + event: 'ping' | 'pong', + listener: (this: WebSocket, data: Buffer) => void + ): this + on( + event: 'unexpected-response', + listener: ( + this: WebSocket, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + on( + event: string | symbol, + listener: (this: WebSocket, ...args: any[]) => void + ): this + + once( + event: 'close', + listener: (this: WebSocket, code: number, reason: string) => void + ): this + once(event: 'error', listener: (this: WebSocket, err: Error) => void): this + once( + event: 'upgrade', + listener: (this: WebSocket, request: IncomingMessage) => void + ): this + once( + event: 'message', + listener: (this: WebSocket, data: WebSocket.Data) => void + ): this + once(event: 'open', listener: (this: WebSocket) => void): this + once( + event: 'ping' | 'pong', + listener: (this: WebSocket, data: Buffer) => void + ): this + once( + event: 'unexpected-response', + listener: ( + this: WebSocket, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + once( + event: string | symbol, + listener: (this: WebSocket, ...args: any[]) => void + ): this + + off( + event: 'close', + listener: (this: WebSocket, code: number, reason: string) => void + ): this + off(event: 'error', listener: (this: WebSocket, err: Error) => void): this + off( + event: 'upgrade', + listener: (this: WebSocket, request: IncomingMessage) => void + ): this + off( + event: 'message', + listener: (this: WebSocket, data: WebSocket.Data) => void + ): this + off(event: 'open', listener: (this: WebSocket) => void): this + off( + event: 'ping' | 'pong', + listener: (this: WebSocket, data: Buffer) => void + ): this + off( + event: 'unexpected-response', + listener: ( + this: WebSocket, + request: ClientRequest, + response: IncomingMessage + ) => void + ): this + off( + event: string | symbol, + listener: (this: WebSocket, ...args: any[]) => void + ): this + + addListener( + event: 'close', + listener: (code: number, message: string) => void + ): this + addListener(event: 'error', listener: (err: Error) => void): this + addListener( + event: 'upgrade', + listener: (request: IncomingMessage) => void + ): this + addListener( + event: 'message', + listener: (data: WebSocket.Data) => void + ): this + addListener(event: 'open', listener: () => void): this + addListener(event: 'ping' | 'pong', listener: (data: Buffer) => void): this + addListener( + event: 'unexpected-response', + listener: (request: ClientRequest, response: IncomingMessage) => void + ): this + addListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + + removeListener( + event: 'close', + listener: (code: number, message: string) => void + ): this + removeListener(event: 'error', listener: (err: Error) => void): this + removeListener( + event: 'upgrade', + listener: (request: IncomingMessage) => void + ): this + removeListener( + event: 'message', + listener: (data: WebSocket.Data) => void + ): this + removeListener(event: 'open', listener: () => void): this + removeListener( + event: 'ping' | 'pong', + listener: (data: Buffer) => void + ): this + removeListener( + event: 'unexpected-response', + listener: (request: ClientRequest, response: IncomingMessage) => void + ): this + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + } + + /** + * Data represents the message payload received over the WebSocket. + */ + type Data = string | Buffer | ArrayBuffer | Buffer[] + + /** + * CertMeta represents the accepted types for certificate & key data. + */ + type CertMeta = string | string[] | Buffer | Buffer[] + + /** + * VerifyClientCallbackSync is a synchronous callback used to inspect the + * incoming message. The return value (boolean) of the function determines + * whether or not to accept the handshake. + */ + type VerifyClientCallbackSync = (info: { + origin: string + secure: boolean + req: IncomingMessage + }) => boolean + + /** + * VerifyClientCallbackAsync is an asynchronous callback used to inspect the + * incoming message. The return value (boolean) of the function determines + * whether or not to accept the handshake. + */ + type VerifyClientCallbackAsync = ( + info: { origin: string; secure: boolean; req: IncomingMessage }, + callback: ( + res: boolean, + code?: number, + message?: string, + headers?: OutgoingHttpHeaders + ) => void + ) => void + + interface ClientOptions extends SecureContextOptions { + protocol?: string | undefined + followRedirects?: boolean | undefined + handshakeTimeout?: number | undefined + maxRedirects?: number | undefined + perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined + localAddress?: string | undefined + protocolVersion?: number | undefined + headers?: { [key: string]: string } | undefined + origin?: string | undefined + agent?: Agent | undefined + host?: string | undefined + family?: number | undefined + checkServerIdentity?(servername: string, cert: CertMeta): boolean + rejectUnauthorized?: boolean | undefined + maxPayload?: number | undefined + } + + interface PerMessageDeflateOptions { + serverNoContextTakeover?: boolean | undefined + clientNoContextTakeover?: boolean | undefined + serverMaxWindowBits?: number | undefined + clientMaxWindowBits?: number | undefined + zlibDeflateOptions?: + | { + flush?: number | undefined + finishFlush?: number | undefined + chunkSize?: number | undefined + windowBits?: number | undefined + level?: number | undefined + memLevel?: number | undefined + strategy?: number | undefined + dictionary?: Buffer | Buffer[] | DataView | undefined + info?: boolean | undefined + } + | undefined + zlibInflateOptions?: ZlibOptions | undefined + threshold?: number | undefined + concurrencyLimit?: number | undefined + } + + interface OpenEvent { + type: string + target: WebSocket + } + + interface ErrorEvent { + error: any + message: string + type: string + target: WebSocket + } + + interface CloseEvent { + wasClean: boolean + code: number + reason: string + type: string + target: WebSocket + } + + interface MessageEvent { + data: Data + type: string + target: WebSocket + } + + interface EventListenerOptions { + once?: boolean | undefined + } + + interface ServerOptions { + host?: string | undefined + port?: number | undefined + backlog?: number | undefined + server?: HTTPServer | HTTPSServer | undefined + verifyClient?: + | VerifyClientCallbackAsync + | VerifyClientCallbackSync + | undefined + handleProtocols?: any + path?: string | undefined + noServer?: boolean | undefined + clientTracking?: boolean | undefined + perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined + maxPayload?: number | undefined + } + + interface AddressInfo { + address: string + family: string + port: number + } + + // WebSocket Server + export class Server extends EventEmitter { + options: ServerOptions + path: string + clients: Set + + constructor(options?: ServerOptions, callback?: () => void) + + address(): AddressInfo | string + close(cb?: (err?: Error) => void): void + handleUpgrade( + request: IncomingMessage, + socket: Socket, + upgradeHead: Buffer, + callback: (client: WebSocket, request: IncomingMessage) => void + ): void + shouldHandle(request: IncomingMessage): boolean | Promise + + // Events + on( + event: 'connection', + cb: (this: Server, socket: WebSocket, request: IncomingMessage) => void + ): this + on(event: 'error', cb: (this: Server, error: Error) => void): this + on( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + on(event: 'close' | 'listening', cb: (this: Server) => void): this + on( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + once( + event: 'connection', + cb: (this: Server, socket: WebSocket, request: IncomingMessage) => void + ): this + once(event: 'error', cb: (this: Server, error: Error) => void): this + once( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + once(event: 'close' | 'listening', cb: (this: Server) => void): this + once(event: string | symbol, listener: (...args: any[]) => void): this + + off( + event: 'connection', + cb: (this: Server, socket: WebSocket, request: IncomingMessage) => void + ): this + off(event: 'error', cb: (this: Server, error: Error) => void): this + off( + event: 'headers', + cb: (this: Server, headers: string[], request: IncomingMessage) => void + ): this + off(event: 'close' | 'listening', cb: (this: Server) => void): this + off( + event: string | symbol, + listener: (this: Server, ...args: any[]) => void + ): this + + addListener( + event: 'connection', + cb: (client: WebSocket, request: IncomingMessage) => void + ): this + addListener(event: 'error', cb: (err: Error) => void): this + addListener( + event: 'headers', + cb: (headers: string[], request: IncomingMessage) => void + ): this + addListener(event: 'close' | 'listening', cb: () => void): this + addListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + + removeListener(event: 'connection', cb: (client: WebSocket) => void): this + removeListener(event: 'error', cb: (err: Error) => void): this + removeListener( + event: 'headers', + cb: (headers: string[], request: IncomingMessage) => void + ): this + removeListener(event: 'close' | 'listening', cb: () => void): this + removeListener( + event: string | symbol, + listener: (...args: any[]) => void + ): this + } + + // WebSocket stream + function createWebSocketStream( + websocket: WebSocket, + options?: DuplexOptions + ): Duplex +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 54bb0b9132f248..113a451ace0a1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -635,6 +635,8 @@ importers: packages/vite: specifiers: '@ampproject/remapping': ^1.0.1 + '@babel/parser': ^7.15.8 + '@babel/types': ^7.15.6 '@rollup/plugin-alias': ^3.1.5 '@rollup/plugin-commonjs': ^21.0.0 '@rollup/plugin-dynamic-import-vars': ^1.4.0 @@ -712,6 +714,8 @@ importers: fsevents: 2.3.2 devDependencies: '@ampproject/remapping': 1.0.1 + '@babel/parser': 7.15.8 + '@babel/types': 7.15.6 '@rollup/plugin-alias': 3.1.5_rollup@2.57.0 '@rollup/plugin-commonjs': 21.0.0_rollup@2.57.0 '@rollup/plugin-dynamic-import-vars': 1.4.0_rollup@2.57.0 @@ -1069,6 +1073,12 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + /@babel/parser/7.15.8: + resolution: {integrity: sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==} + engines: {node: '>=6.0.0'} + hasBin: true + dev: true + /@babel/plugin-proposal-pipeline-operator/7.15.0: resolution: {integrity: sha512-/XNBV8GmMxl7icZ0G5o4f3aGXHDKuhS8xHhbdusjE/ZDrsqtLq5kUiw/i7J6ZnlS/ngM0IQTN2CPlrPTb6GKVw==} engines: {node: '>=6.9.0'}