/// <summary> /// If the current user-agent in the request is eligible for ES5 fallback, return true /// </summary> /// <param name="request"></param> /// <returns></returns> public static bool BrowserNeedES5Fallback(this HttpRequest request) { BlazorPolyfillOptions _options = BlazorPolyfillMiddlewareExtensions.GetOptions(); if (_options.ForceES5Fallback) { return(true); } //In this case, the user is responsible from any delegate crash. //As it does have control over the delegate, the error will be visible for him. //If this call does not return true, we must continue the regular checking workflow if (_options.ES5FallbackValidation != null && _options.ES5FallbackValidation(request)) { return(true); } string userAgent = request.Headers["User-Agent"]; if (userAgent == null) { return(false); } return(IsInternetExplorer(userAgent) || IsEdgeHTML(userAgent)); }
public static IApplicationBuilder UseBlazorPolyfill( this IApplicationBuilder builder, Action <BlazorPolyfillOptions> configureOptions) { if (builder is null) { throw new ArgumentNullException(nameof(builder)); } if (configureOptions is null) { throw new ArgumentNullException(nameof(configureOptions)); } BlazorPolyfillOptions options = new BlazorPolyfillOptions(); //Let the user configure the options in it's delegate method configureOptions(options); return(UseBlazorPolyfill(builder, options)); }
private static FileContentReference GetPatchedBlazorServerFile() { if (_patchedBlazorServerFile == null) { BlazorPolyfillOptions option = GetOptions(); if (option.UsePackagedBlazorServerLibrary) { //Get packaged blazor.server.js var assembly = GetBlazorPolyfillAssembly(); var resources = assembly.GetManifestResourceNames(); var resourceName = resources.Single(str => str.EndsWith("blazor.server.packaged.js")); using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(stream)) { string js = reader.ReadToEnd(); string Etag = CryptographyHelper.CreateSHA256(js); //We should rely on ETag and not on LastModifed informations DateTime buildTime = GetAssemblyCreationDate(assembly); _patchedBlazorServerFile = new FileContentReference() { Value = js, ETag = Etag, LastModified = buildTime, ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(js).ToString(CultureInfo.InvariantCulture) }; } } } else { var assembly = GetAspNetCoreComponentsServerAssembly(); var resources = assembly.GetManifestResourceNames(); var resourceName = resources.Single(str => str.EndsWith("blazor.server.js")); using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(stream)) { string js = reader.ReadToEnd(); #region Patch Regex //Patch Descriptor Regex as it make Babel crash during transform js = js.Replace("/\\W*Blazor:[^{]*(?<descriptor>.*)$/;", @"/[\0-\/:-@\[-\^`\{-\uFFFF]*Blazor:(?:(?!\{)[\s\S])*(.*)$/;"); js = js.Replace("/^\\s*Blazor-Component-State:(?<state>[a-zA-Z0-9\\+\\/=]+)$/", @"/^[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*Blazor\x2DComponent\x2DState:([\+\/-9=A-Za-z]+)$/"); js = js.Replace("/^\\s*Blazor:[^{]*(?<descriptor>.*)$/", @"/^[\t-\r \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF]*Blazor:(?:(?!\{)[\s\S])*(.*)$/"); #endregion Patch Regex //Transpile code to ES5 for IE11 before manual patching js = BabelHelper.Transform(js, "blazor.server.js"); #region Regex named groups fix //At this point, Babel has unminified the code, and fixed IE11 issues, like 'import' method calls. //We still need to fix 'descriptor' regex evaluation code, as it was expecting a named capture group. js = Regex.Replace(js, "([_a-zA-Z0-9]+)(.groups[ ]*&&[ ]*[_a-zA-Z0-9]+.groups.descriptor)", "$1[1]"); //We still need to fix 'state' regex evaluation code, as it was expecting a named capture group. js = Regex.Replace(js, "([_a-zA-Z0-9]+)(.groups[ ]*&&[ ]*[_a-zA-Z0-9]+.groups.state)", "$1[1]"); //Here we fix invalids interopRequireWildcard(require(''.concat(n))) to _interopRequireWildcard(''.concat(n)) (works for '' or "") //Warning: " is written "" here but must be read as " from the regex logic: We are in a verbatim string js = Regex.Replace(js, @"(require\((['""]['""].concat\([a-zA-Z]+\))\))", "$2"); #endregion Regex named groups fix //Minify with AjaxMin (we don't want an additional external tool with NPM or else for managing this //kind of thing here... js = Uglify.Js(js).Code; //Computing ETag. Should be computed last ! string Etag = CryptographyHelper.CreateSHA256(js); //Computing Build time for the Last-Modified Http Header //We should rely on the creation date of the Microsoft API //not the Blazor.Polyfill.Server one as the Microsoft.AspNetCore.Components.Server //assembly may be updated in time. We will rely on the current creation/modification date on disk DateTime buildTime = GetAssemblyCreationDate(assembly); _patchedBlazorServerFile = new FileContentReference() { Value = js, ETag = Etag, LastModified = buildTime, ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(js).ToString(CultureInfo.InvariantCulture) }; } } } } return(_patchedBlazorServerFile); }
public static IApplicationBuilder UseBlazorPolyfill( this IApplicationBuilder builder, BlazorPolyfillOptions options) { if (builder is null) { throw new ArgumentNullException(nameof(builder)); } if (options is null) { throw new ArgumentNullException(nameof(options)); } SetOptions(options); InitReact(builder); //This is a kind of hack for caching the data at boot //It's better to prevent anything at first request by caching instead of //making the user wait the file generation builder.Use((context, next) => { if (!IsBlazorPolyfillLibCached()) { //Avoiding first client to await the file generation CacheBlazorPolyfillLib(); } //Normal behavior return(next()); }); //This is used in order to transpile dynamically StaticFiles to ES5 when needed builder.UseMiddleware <ECMAScript5Middleware>(); //BrowserNeedES5Fallback is written hiere and not in the builder //because we only want to use MapWhen when we need ES5 fallback. //If this is false, the request will be redirected to the Microsoft //default request management for this file. builder.MapWhen(ctx => ECMAScript5Middleware.RequestedFileIsBlazorServerJS(ctx.Request) && ctx.Request.BrowserNeedES5Fallback(), subBuilder => { subBuilder.Run(async(context) => { var fileContent = GetPatchedBlazorServerFile(); await HttpRequestManager.ManageRequest(context, fileContent); }); }); //As blazor.polyfill.js files does not exist really on theses path and //does not have a real fallback request mangement (as for blazor.server.js) //we should intercept them at any time with MapWhen, but change the result //behavior lately in the builder. Otherwise this would return a 404 error. builder.MapWhen(ctx => ECMAScript5Middleware.RequestedFileIsBlazorPolyfill(ctx.Request, out bool isMinified), subBuilder => { subBuilder.Run(async(context) => { ECMAScript5Middleware.RequestedFileIsBlazorPolyfill(context.Request, out bool isMinified); //Eval if the requested file is the minified version or not var fileContent = GetBlazorPolyfillFile(context.Request.BrowserNeedES5Fallback(), isMinified); await HttpRequestManager.ManageRequest(context, fileContent); }); }); return(builder); }
private static void SetOptions(BlazorPolyfillOptions options) { _options = options; }
private static FileContentReference GetPatchedBlazorServerFile() { if (_patchedBlazorServerFile == null) { BlazorPolyfillOptions option = GetOptions(); if (option.UsePackagedBlazorServerLibrary) { //Get packaged blazor.server.js var assembly = GetBlazorPolyfillAssembly(); var resources = assembly.GetManifestResourceNames(); var resourceName = resources.Single(str => str.EndsWith("blazor.server.packaged.js")); using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(stream)) { string js = reader.ReadToEnd(); string Etag = EtagGenerator.GenerateEtagFromString(js); //Computing Build time for the Last-Modified Http Header //We should rely on the creation date of the Microsoft API //not the Blazor.Polyfill.Server one as the Microsoft.AspNetCore.Components.Server //assembly may be updated in time. We will rely on the current creation/modification date on disk DateTime buildTime = GetAssemblyCreationDate(assembly); _patchedBlazorServerFile = new FileContentReference() { Value = js, ETag = Etag, LastModified = buildTime, ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(js).ToString(CultureInfo.InvariantCulture) }; } } } else { var assembly = GetAspNetCoreComponentsServerAssembly(); var resources = assembly.GetManifestResourceNames(); var resourceName = resources.Single(str => str.EndsWith("blazor.server.js")); using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(stream)) { string js = reader.ReadToEnd(); //Patch Descriptor Regex as it make Babel crash during transform js = js.Replace("/\\W*Blazor:[^{]*(?<descriptor>.*)$/;", @"/[\0-\/:-@\[-\^`\{-\uFFFF]*Blazor:(?:(?!\{)[\s\S])*(.*)$/;"); //Transpile code to ES5 for IE11 before manual patching js = Transform(js, "blazor.server.js", "{\"plugins\":[\"proposal-class-properties\",\"proposal-object-rest-spread\"],\"presets\":[[\"env\",{\"targets\":{\"browsers\":[\"ie 11\"]}}], \"es2015\",\"es2016\",\"es2017\",\"stage-3\"], \"sourceType\": \"script\"}"); //At this point, Babel has unminified the code, and fixed IE11 issues, like 'import' method calls. //We still need to fix 'descriptor' regex evaluation code, as it was expecting a named capture group. js = Regex.Replace(js, "([a-zA-Z]+)(.groups[ ]*&&[ ]*[a-zA-Z]+.groups.descriptor)", "$1[1]"); //Minify with AjaxMin (we don't want an additional external tool with NPM or else for managing this //kind of thing here... js = Uglify.Js(js).Code; //Computing ETag. Should be computed last ! string Etag = EtagGenerator.GenerateEtagFromString(js); //Computing Build time for the Last-Modified Http Header //We should rely on the creation date of the Microsoft API //not the Blazor.Polyfill.Server one as the Microsoft.AspNetCore.Components.Server //assembly may be updated in time. We will rely on the current creation/modification date on disk DateTime buildTime = GetAssemblyCreationDate(assembly); _patchedBlazorServerFile = new FileContentReference() { Value = js, ETag = Etag, LastModified = buildTime, ContentLength = System.Text.UTF8Encoding.UTF8.GetByteCount(js).ToString(CultureInfo.InvariantCulture) }; } } } } return(_patchedBlazorServerFile); }