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
This commit is contained in:
Maxim Dukhanov 2019-02-08 05:51:24 +03:00 committed by Stephen Halter
parent 3d4b198990
commit 095c1c1759
8 changed files with 60 additions and 17 deletions

View File

@ -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

View File

@ -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"
},

View File

@ -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}'.`);
}

View File

@ -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

View File

@ -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 = "";

View File

@ -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: {

View File

@ -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",

View File

@ -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,