Fix SSR issues for SignalR: require is not defined (#19832)

This commit is contained in:
Steve 2020-03-24 00:32:58 +08:00 committed by GitHub
parent 44230d36a7
commit 56d50e677b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 44 additions and 42 deletions

View File

@ -9,35 +9,36 @@ import { HttpClient, HttpRequest, HttpResponse } from "./HttpClient";
import { ILogger, LogLevel } from "./ILogger"; import { ILogger, LogLevel } from "./ILogger";
import { Platform } from "./Utils"; import { Platform } from "./Utils";
let abortControllerType: { prototype: AbortController, new(): AbortController };
let fetchType: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
let jar: tough.CookieJar;
if (typeof fetch === "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;
// Cookies aren't automatically handled in Node so we need to add a CookieJar to preserve cookies across requests
jar = new (requireFunc("tough-cookie")).CookieJar();
fetchType = requireFunc("node-fetch");
// node-fetch doesn't have a nice API for getting and setting cookies
// fetch-cookie will wrap a fetch implementation with a default CookieJar or a provided one
fetchType = requireFunc("fetch-cookie")(fetchType, jar);
// Node needs EventListener methods on AbortController which our custom polyfill doesn't provide
abortControllerType = requireFunc("abort-controller");
} else {
fetchType = fetch;
abortControllerType = AbortController;
}
export class FetchHttpClient extends HttpClient { export class FetchHttpClient extends HttpClient {
private readonly abortControllerType: { prototype: AbortController, new(): AbortController };
private readonly fetchType: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
private readonly jar?: tough.CookieJar;
private readonly logger: ILogger; private readonly logger: ILogger;
public constructor(logger: ILogger) { public constructor(logger: ILogger) {
super(); super();
this.logger = logger; this.logger = logger;
if (typeof fetch === "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;
// Cookies aren't automatically handled in Node so we need to add a CookieJar to preserve cookies across requests
this.jar = new (requireFunc("tough-cookie")).CookieJar();
this.fetchType = requireFunc("node-fetch");
// node-fetch doesn't have a nice API for getting and setting cookies
// fetch-cookie will wrap a fetch implementation with a default CookieJar or a provided one
this.fetchType = requireFunc("fetch-cookie")(this.fetchType, this.jar);
// Node needs EventListener methods on AbortController which our custom polyfill doesn't provide
this.abortControllerType = requireFunc("abort-controller");
} else {
this.fetchType = fetch.bind(self);
this.abortControllerType = AbortController;
}
} }
/** @inheritDoc */ /** @inheritDoc */
@ -54,7 +55,7 @@ export class FetchHttpClient extends HttpClient {
throw new Error("No url defined."); throw new Error("No url defined.");
} }
const abortController = new abortControllerType(); const abortController = new this.abortControllerType();
let error: any; let error: any;
// Hook our abortSignal into the abort controller // Hook our abortSignal into the abort controller
@ -79,7 +80,7 @@ export class FetchHttpClient extends HttpClient {
let response: Response; let response: Response;
try { try {
response = await fetchType(request.url!, { response = await this.fetchType(request.url!, {
body: request.content!, body: request.content!,
cache: "no-cache", cache: "no-cache",
credentials: request.withCredentials === true ? "include" : "same-origin", credentials: request.withCredentials === true ? "include" : "same-origin",
@ -127,9 +128,9 @@ export class FetchHttpClient extends HttpClient {
public getCookieString(url: string): string { public getCookieString(url: string): string {
let cookies: string = ""; let cookies: string = "";
if (Platform.isNode) { if (Platform.isNode && this.jar) {
// @ts-ignore: unused variable // @ts-ignore: unused variable
jar.getCookies(url, (e, c) => cookies = c.join("; ")); this.jar.getCookies(url, (e, c) => cookies = c.join("; "));
} }
return cookies; return cookies;
} }

View File

@ -14,7 +14,7 @@ import { WebSocketTransport } from "./WebSocketTransport";
/** @private */ /** @private */
const enum ConnectionState { const enum ConnectionState {
Connecting = "Connecting ", Connecting = "Connecting",
Connected = "Connected", Connected = "Connected",
Disconnected = "Disconnected", Disconnected = "Disconnected",
Disconnecting = "Disconnecting", Disconnecting = "Disconnecting",
@ -39,16 +39,6 @@ export interface IAvailableTransport {
const MAX_REDIRECTS = 100; const MAX_REDIRECTS = 100;
let WebSocketModule: any = null;
let EventSourceModule: any = null;
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;
WebSocketModule = requireFunc("ws");
EventSourceModule = requireFunc("eventsource");
}
/** @private */ /** @private */
export class HttpConnection implements IConnection { export class HttpConnection implements IConnection {
private connectionState: ConnectionState; private connectionState: ConnectionState;
@ -88,19 +78,30 @@ export class HttpConnection implements IConnection {
throw new Error("withCredentials option was not a 'boolean' or 'undefined' value"); throw new Error("withCredentials option was not a 'boolean' or 'undefined' value");
} }
let webSocketModule: any = null;
let eventSourceModule: any = null;
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;
webSocketModule = requireFunc("ws");
eventSourceModule = requireFunc("eventsource");
}
if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) { if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) {
options.WebSocket = WebSocket; options.WebSocket = WebSocket;
} else if (Platform.isNode && !options.WebSocket) { } else if (Platform.isNode && !options.WebSocket) {
if (WebSocketModule) { if (webSocketModule) {
options.WebSocket = WebSocketModule; options.WebSocket = webSocketModule;
} }
} }
if (!Platform.isNode && typeof EventSource !== "undefined" && !options.EventSource) { if (!Platform.isNode && typeof EventSource !== "undefined" && !options.EventSource) {
options.EventSource = EventSource; options.EventSource = EventSource;
} else if (Platform.isNode && !options.EventSource) { } else if (Platform.isNode && !options.EventSource) {
if (typeof EventSourceModule !== "undefined") { if (typeof eventSourceModule !== "undefined") {
options.EventSource = EventSourceModule; options.EventSource = eventSourceModule;
} }
} }