From 095c1c1759daef34e18a94c86236e4f02644ebe9 Mon Sep 17 00:00:00 2001 From: Maxim Dukhanov <m.dukhanov@gmail.com> Date: Fri, 8 Feb 2019 05:51:24 +0300 Subject: [PATCH] Add webworker support to SignalR JS client (#7058) * Added Platform utils to detect platform type * Added additional build for WebWorker * Changed env param from webworker to platform to make ability to specify platform to the build script * Updated the readme file with SignalR WebWorker instructions --- src/SignalR/clients/ts/signalr/README.md | 23 +++++++++++++++++++ src/SignalR/clients/ts/signalr/package.json | 7 ++++-- .../clients/ts/signalr/src/HttpConnection.ts | 15 ++++++------ .../signalr/src/ServerSentEventsTransport.ts | 4 ++-- src/SignalR/clients/ts/signalr/src/Utils.ts | 16 +++++++++++++ .../ts/signalr/src/WebSocketTransport.ts | 4 ++-- .../clients/ts/signalr/webpack.config.js | 5 ++-- src/SignalR/clients/ts/webpack.config.base.js | 3 ++- 8 files changed, 60 insertions(+), 17 deletions(-) diff --git a/src/SignalR/clients/ts/signalr/README.md b/src/SignalR/clients/ts/signalr/README.md index 51105078a26..f38a93401ed 100644 --- a/src/SignalR/clients/ts/signalr/README.md +++ b/src/SignalR/clients/ts/signalr/README.md @@ -14,6 +14,10 @@ See the [SignalR Documentation](https://docs.microsoft.com/en-us/aspnet/core/sig To use the client in a browser, copy `*.js` files from the `dist/browser` folder to your script folder include on your page using the `<script>` tag. +### WebWorker + +To use the client in a webworker, copy `*.js` files from the `dist/webworker` folder to your script folder include on your webworker using the `importScripts` function. Note that webworker SignalR hub connection supports only absolute path to a SignalR hub. + ### Node.js To use the client in a NodeJS application, install the package to your `node_modules` folder and use `require('@aspnet/signalr')` to load the module. The object returned by `require('@aspnet/signalr')` has the same members as the global `signalR` object (when used in a browser). @@ -33,6 +37,25 @@ connection.start() .then(() => connection.invoke("send", "Hello")); ``` +### Example (WebWorker) + + +```JavaScript +importScripts('signalr.js'); + +let connection = new signalR.HubConnectionBuilder() + .withUrl("https://example.com/signalr/chat") + .build(); + +connection.on("send", data => { + console.log(data); +}); + +connection.start() + .then(() => connection.invoke("send", "Hello")); + +``` + ### Example (NodeJS) ```JavaScript diff --git a/src/SignalR/clients/ts/signalr/package.json b/src/SignalR/clients/ts/signalr/package.json index 83acbb358a5..c18ff51d0b4 100644 --- a/src/SignalR/clients/ts/signalr/package.json +++ b/src/SignalR/clients/ts/signalr/package.json @@ -12,12 +12,15 @@ }, "scripts": { "clean": "node ../common/node_modules/rimraf/bin.js ./dist", - "build": "npm run clean && npm run build:lint && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:uglify", + "build": "npm run clean && npm run build:lint && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:webworker && npm run build:uglify", "build:lint": "node ../common/node_modules/tslint/bin/tslint -c ../tslint.json -p ./tsconfig.json", "build:esm": "node ../common/node_modules/typescript/bin/tsc --project ./tsconfig.json --module es2015 --outDir ./dist/esm -d && node ./build/process-dts.js", "build:cjs": "node ../common/node_modules/typescript/bin/tsc --project ./tsconfig.json --module commonjs --outDir ./dist/cjs", "build:browser": "node ../common/node_modules/webpack-cli/bin/cli.js", - "build:uglify": "node ../common/node_modules/uglify-js/bin/uglifyjs --source-map \"url='signalr.min.js.map',content='./dist/browser/signalr.js.map'\" --comments -o ./dist/browser/signalr.min.js ./dist/browser/signalr.js", + "build:webworker": "node ../common/node_modules/webpack-cli/bin/cli.js --env.platform=webworker", + "build:uglify": "npm run build:uglify:browser && npm run build:uglify:webworker", + "build:uglify:browser": "node ../common/node_modules/uglify-js/bin/uglifyjs --source-map \"url='signalr.min.js.map',content='./dist/browser/signalr.js.map'\" --comments -o ./dist/browser/signalr.min.js ./dist/browser/signalr.js", + "build:uglify:webworker": "node ../common/node_modules/uglify-js/bin/uglifyjs --source-map \"url='signalr.min.js.map',content='./dist/webworker/signalr.js.map'\" --comments -o ./dist/webworker/signalr.min.js ./dist/webworker/signalr.js", "prepack": "node ../build/embed-version.js", "test": "echo \"Run 'npm test' in the 'clients\\ts' folder to test this package\" && exit 1" }, diff --git a/src/SignalR/clients/ts/signalr/src/HttpConnection.ts b/src/SignalR/clients/ts/signalr/src/HttpConnection.ts index d3521f4f858..2d74b80ef39 100644 --- a/src/SignalR/clients/ts/signalr/src/HttpConnection.ts +++ b/src/SignalR/clients/ts/signalr/src/HttpConnection.ts @@ -9,7 +9,7 @@ import { ILogger, LogLevel } from "./ILogger"; import { HttpTransportType, ITransport, TransferFormat } from "./ITransport"; import { LongPollingTransport } from "./LongPollingTransport"; import { ServerSentEventsTransport } from "./ServerSentEventsTransport"; -import { Arg, createLogger } from "./Utils"; +import { Arg, createLogger, Platform } from "./Utils"; import { WebSocketTransport } from "./WebSocketTransport"; /** @private */ @@ -38,7 +38,7 @@ const MAX_REDIRECTS = 100; let WebSocketModule: any = null; let EventSourceModule: any = null; -if (typeof window === "undefined" && typeof require !== "undefined") { +if (Platform.isNode && typeof require !== "undefined") { // In order to ignore the dynamic require in webpack builds we need to do this magic // @ts-ignore: TS doesn't know about these names const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require; @@ -71,18 +71,17 @@ export class HttpConnection implements IConnection { options = options || {}; options.logMessageContent = options.logMessageContent || false; - const isNode = typeof window === "undefined"; - if (!isNode && typeof WebSocket !== "undefined" && !options.WebSocket) { + if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) { options.WebSocket = WebSocket; - } else if (isNode && !options.WebSocket) { + } else if (Platform.isNode && !options.WebSocket) { if (WebSocketModule) { options.WebSocket = WebSocketModule; } } - if (!isNode && typeof EventSource !== "undefined" && !options.EventSource) { + if (!Platform.isNode && typeof EventSource !== "undefined" && !options.EventSource) { options.EventSource = EventSource; - } else if (isNode && !options.EventSource) { + } else if (Platform.isNode && !options.EventSource) { if (typeof EventSourceModule !== "undefined") { options.EventSource = EventSourceModule; } @@ -383,7 +382,7 @@ export class HttpConnection implements IConnection { return url; } - if (typeof window === "undefined" || !window || !window.document) { + if (!Platform.isBrowser || !window.document) { throw new Error(`Cannot resolve '${url}'.`); } diff --git a/src/SignalR/clients/ts/signalr/src/ServerSentEventsTransport.ts b/src/SignalR/clients/ts/signalr/src/ServerSentEventsTransport.ts index ee2de43ef59..09463c5e70d 100644 --- a/src/SignalR/clients/ts/signalr/src/ServerSentEventsTransport.ts +++ b/src/SignalR/clients/ts/signalr/src/ServerSentEventsTransport.ts @@ -5,7 +5,7 @@ import { HttpClient } from "./HttpClient"; import { ILogger, LogLevel } from "./ILogger"; import { ITransport, TransferFormat } from "./ITransport"; import { EventSourceConstructor } from "./Polyfills"; -import { Arg, getDataDetail, sendMessage } from "./Utils"; +import { Arg, getDataDetail, Platform, sendMessage } from "./Utils"; /** @private */ export class ServerSentEventsTransport implements ITransport { @@ -57,7 +57,7 @@ export class ServerSentEventsTransport implements ITransport { } let eventSource: EventSource; - if (typeof window !== "undefined") { + if (Platform.isBrowser || Platform.isWebWorker) { eventSource = new this.eventSourceConstructor(url, { withCredentials: true }); } else { // Non-browser passes cookies via the dictionary diff --git a/src/SignalR/clients/ts/signalr/src/Utils.ts b/src/SignalR/clients/ts/signalr/src/Utils.ts index 23780eafa8c..b4e6a78c37e 100644 --- a/src/SignalR/clients/ts/signalr/src/Utils.ts +++ b/src/SignalR/clients/ts/signalr/src/Utils.ts @@ -23,6 +23,22 @@ export class Arg { } } +/** @private */ +export class Platform { + + public static get isBrowser(): boolean { + return typeof window === "object"; + } + + public static get isWebWorker(): boolean { + return typeof self === "object" && "importScripts" in self; + } + + public static get isNode(): boolean { + return !this.isBrowser && !this.isWebWorker; + } +} + /** @private */ export function getDataDetail(data: any, includeContent: boolean): string { let detail = ""; diff --git a/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts b/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts index 74db953b5ab..a9c238599eb 100644 --- a/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts +++ b/src/SignalR/clients/ts/signalr/src/WebSocketTransport.ts @@ -5,7 +5,7 @@ import { HttpClient } from "./HttpClient"; import { ILogger, LogLevel } from "./ILogger"; import { ITransport, TransferFormat } from "./ITransport"; import { WebSocketConstructor } from "./Polyfills"; -import { Arg, getDataDetail } from "./Utils"; +import { Arg, getDataDetail, Platform } from "./Utils"; /** @private */ export class WebSocketTransport implements ITransport { @@ -50,7 +50,7 @@ export class WebSocketTransport implements ITransport { let webSocket: WebSocket | undefined; const cookies = this.httpClient.getCookieString(url); - if (typeof window === "undefined" && cookies) { + if (Platform.isNode && cookies) { // Only pass cookies when in non-browser environments webSocket = new this.webSocketConstructor(url, undefined, { headers: { diff --git a/src/SignalR/clients/ts/signalr/webpack.config.js b/src/SignalR/clients/ts/signalr/webpack.config.js index 54e3456aac6..75ce866f898 100644 --- a/src/SignalR/clients/ts/signalr/webpack.config.js +++ b/src/SignalR/clients/ts/signalr/webpack.config.js @@ -1,10 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - const baseConfig = require("../webpack.config.base"); -module.exports = baseConfig(__dirname, "signalr", { +module.exports = env => baseConfig(__dirname, "signalr", { // These are only used in Node environments // so we tell webpack not to pull them in for the browser + target: env && env.platform ? env.platform : undefined, + platformDist: env && env.platform ? env.platform : undefined, externals: [ "websocket", "eventsource", diff --git a/src/SignalR/clients/ts/webpack.config.base.js b/src/SignalR/clients/ts/webpack.config.base.js index 6779afc2ff3..8c8f3ac31f9 100644 --- a/src/SignalR/clients/ts/webpack.config.base.js +++ b/src/SignalR/clients/ts/webpack.config.base.js @@ -17,6 +17,7 @@ module.exports = function (modulePath, browserBaseName, options) { process: false, Buffer: false, }, + target: options.target, resolveLoader: { // Special resolution rules for loaders (which are in the 'common' directory) modules: [ path.resolve(__dirname, "common", "node_modules") ], @@ -43,7 +44,7 @@ module.exports = function (modulePath, browserBaseName, options) { }, output: { filename: `${browserBaseName}.js`, - path: path.resolve(modulePath, "dist", "browser"), + path: path.resolve(modulePath, "dist", options.platformDist || "browser"), library: { root: pkg.umd_name.split("."), amd: pkg.umd_name, -- GitLab