diff --git a/src/Components/Web.JS/dist/Release/blazor.webassembly.js b/src/Components/Web.JS/dist/Release/blazor.webassembly.js index f20bfe7c15..fe19d38dd5 100644 --- a/src/Components/Web.JS/dist/Release/blazor.webassembly.js +++ b/src/Components/Web.JS/dist/Release/blazor.webassembly.js @@ -1 +1 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=52)}([,,,,,,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(30),n(23);var r=n(31),o=n(18),a={},i=!1;function u(e,t,n){var o=a[e];o||(o=a[e]=new r.BrowserRenderer(e)),o.attachRootComponentToLogicalElement(n,t)}t.attachRootComponentToLogicalElement=u,t.attachRootComponentToElement=function(e,t,n){var r=document.querySelector(e);if(!r)throw new Error("Could not find any element matching selector '"+e+"'.");u(n||0,o.toLogicalElement(r,!0),t)},t.renderBatch=function(e,t){var n=a[e];if(!n)throw new Error("There is no browser renderer with ID "+e+".");for(var r=t.arrayRangeReader,o=t.updatedComponents(),u=r.values(o),s=r.count(o),l=t.referenceFrames(),c=r.values(l),d=t.diffReader,f=0;f0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function u(e,t,n){var a=e;if(e instanceof Comment&&(l(a)&&l(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(s(a))throw new Error("Not implemented: moving existing logical children");var i=l(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=s,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return l(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===c(e).namespaceURI},t.getLogicalChildrenArray=l,t.permuteLogicalChildren=function(e,t){var n=l(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=d(t);if(n)return n.previousSibling;var r=s(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):f(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=c},,,,function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t,n,r){var o=l();if(o.invokeDotNetFromJS){var a=JSON.stringify(r,m),i=o.invokeDotNetFromJS(e,t,n,a);return i?d(i):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function s(e,t,r,a){if(e&&r)throw new Error("For instance method calls, assemblyName should be null. Received '"+e+"'.");var i=o++,u=new Promise(function(e,t){n[i]={resolve:e,reject:t}});try{var s=JSON.stringify(a,m);l().beginInvokeDotNetFromJS(i,e,t,r,s)}catch(e){c(i,!1,e)}return u}function l(){if(null!==a)return a;throw new Error("No .NET call dispatcher has been set.")}function c(e,t,r){if(!n.hasOwnProperty(e))throw new Error("There is no pending async call with ID "+e+".");var o=n[e];delete n[e],t?o.resolve(r):o.reject(r)}function d(e){return e?JSON.parse(e,function(e,n){return t.reduce(function(t,n){return n(e,t)},n)}):null}function f(e){return e instanceof Error?e.message+"\n"+e.stack:e?e.toString():"null"}function p(e){if(r.hasOwnProperty(e))return r[e];var t,n=window,o="window";if(e.split(".").forEach(function(e){if(!(e in n))throw new Error("Could not find '"+e+"' in '"+o+"'.");t=n,n=n[e],o+="."+e}),n instanceof Function)return n=n.bind(t),r[e]=n,n;throw new Error("The value '"+o+"' is not a function.")}e.attachDispatcher=function(e){a=e},e.attachReviver=i,e.invokeMethod=function(e,t){for(var n=[],r=2;r0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]>2,r=Module.HEAPU32[n+1];if(r>d)throw new Error("Cannot read uint64 with high order part "+r+", because the result would exceed Number.MAX_SAFE_INTEGER.");return r*c+Module.HEAPU32[n]},readFloatField:function(e,t){return Module.getValue(e+(t||0),"float")},readObjectField:function(e,t){return Module.getValue(e+(t||0),"i32")},readStringField:function(e,n){var r=Module.getValue(e+(n||0),"i32");return 0===r?null:t.monoPlatform.toJavaScriptString(r)},readStructField:function(e,t){return e+(t||0)}};var f=document.createElement("a");function p(e){return e+12}function h(e,t,n){var r="["+e+"] "+t+":"+n;return Module.mono_bind_static_method(r)}function m(e,t){return r(this,void 0,void 0,function(){var n,r;return o(this,function(o){switch(o.label){case 0:if("function"!=typeof WebAssembly.instantiateStreaming)return[3,4];o.label=1;case 1:return o.trys.push([1,3,,4]),[4,WebAssembly.instantiateStreaming(e.response,t)];case 2:return[2,o.sent().instance];case 3:return n=o.sent(),console.info("Streaming compilation failed. Falling back to ArrayBuffer instantiation. ",n),[3,4];case 4:return[4,e.response.then(function(e){return e.arrayBuffer()})];case 5:return r=o.sent(),[4,WebAssembly.instantiate(r,t)];case 6:return[2,o.sent().instance]}})})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=window.chrome&&navigator.userAgent.indexOf("Edge")<0,o=!1;function a(){return o&&r}t.hasDebuggingEnabled=a,t.attachDebuggerHotkey=function(e){var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";a()&&console.info("Debugging hotkey: Shift+"+t+"+D (when application has focus)"),o=!!e.bootConfig.resources.pdb,document.addEventListener("keydown",function(e){var t;e.shiftKey&&(e.metaKey||e.altKey)&&"KeyD"===e.code&&(o?r?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("Currently, only Edge(Chromium) or Chrome is supported for debugging."):console.error("Cannot start debugging, because the application was not compiled with debugging enabled."))})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(23),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=a,this.arrayBuilderSegmentReader=i,this.diffReader=u,this.editReader=s,this.frameReader=l}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,a.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*a.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*a.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return c(e,t,u.structLength)},e.prototype.referenceFramesEntry=function(e,t){return c(e,t,l.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=c(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=c(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var a={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},i={structLength:12,values:function(e){var t=r.platform.readObjectField(e,0),n=r.platform.getObjectFieldsBaseAddress(t);return r.platform.readObjectField(n,0)},offset:function(e){return r.platform.readInt32Field(e,4)},count:function(e){return r.platform.readInt32Field(e,8)}},u={structLength:4+i.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return c(e,t,s.structLength)}},s={structLength:20,editType:function(e){return r.platform.readInt32Field(e,0)},siblingIndex:function(e){return r.platform.readInt32Field(e,4)},newTreeIndex:function(e){return r.platform.readInt32Field(e,8)},moveToSiblingIndex:function(e){return r.platform.readInt32Field(e,8)},removedAttributeName:function(e){return r.platform.readStringField(e,16)}},l={structLength:36,frameType:function(e){return r.platform.readInt16Field(e,4)},subtreeLength:function(e){return r.platform.readInt32Field(e,8)},elementReferenceCaptureId:function(e){return r.platform.readStringField(e,16)},componentId:function(e){return r.platform.readInt32Field(e,12)},elementName:function(e){return r.platform.readStringField(e,16)},textContent:function(e){return r.platform.readStringField(e,16)},markupContent:function(e){return r.platform.readStringField(e,16)},attributeName:function(e){return r.platform.readStringField(e,16)},attributeValue:function(e){return r.platform.readStringField(e,24)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function c(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(i,u)}s((r=r.apply(e,t||[])).next())})},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=(o=i.trys).length>0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function u(e,t,n){var a=e;if(e instanceof Comment&&(l(a)&&l(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(s(a))throw new Error("Not implemented: moving existing logical children");var i=l(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=s,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return l(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===c(e).namespaceURI},t.getLogicalChildrenArray=l,t.permuteLogicalChildren=function(e,t){var n=l(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=s(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=c},,,,function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t,n,r){var o=l();if(o.invokeDotNetFromJS){var a=JSON.stringify(r,m),i=o.invokeDotNetFromJS(e,t,n,a);return i?f(i):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function s(e,t,r,a){if(e&&r)throw new Error("For instance method calls, assemblyName should be null. Received '"+e+"'.");var i=o++,u=new Promise(function(e,t){n[i]={resolve:e,reject:t}});try{var s=JSON.stringify(a,m);l().beginInvokeDotNetFromJS(i,e,t,r,s)}catch(e){c(i,!1,e)}return u}function l(){if(null!==a)return a;throw new Error("No .NET call dispatcher has been set.")}function c(e,t,r){if(!n.hasOwnProperty(e))throw new Error("There is no pending async call with ID "+e+".");var o=n[e];delete n[e],t?o.resolve(r):o.reject(r)}function f(e){return e?JSON.parse(e,function(e,n){return t.reduce(function(t,n){return n(e,t)},n)}):null}function d(e){return e instanceof Error?e.message+"\n"+e.stack:e?e.toString():"null"}function p(e){if(r.hasOwnProperty(e))return r[e];var t,n=window,o="window";if(e.split(".").forEach(function(e){if(!(e in n))throw new Error("Could not find '"+e+"' in '"+o+"'.");t=n,n=n[e],o+="."+e}),n instanceof Function)return n=n.bind(t),r[e]=n,n;throw new Error("The value '"+o+"' is not a function.")}e.attachDispatcher=function(e){a=e},e.attachReviver=i,e.invokeMethod=function(e,t){for(var n=[],r=2;r0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]>2,r=Module.HEAPU32[n+1];if(r>f)throw new Error("Cannot read uint64 with high order part "+r+", because the result would exceed Number.MAX_SAFE_INTEGER.");return r*c+Module.HEAPU32[n]},readFloatField:function(e,t){return Module.getValue(e+(t||0),"float")},readObjectField:function(e,t){return Module.getValue(e+(t||0),"i32")},readStringField:function(e,n){var r=Module.getValue(e+(n||0),"i32");return 0===r?null:t.monoPlatform.toJavaScriptString(r)},readStructField:function(e,t){return e+(t||0)}};var d=document.createElement("a");function p(e){return e+12}function h(e,t,n){var r="["+e+"] "+t+":"+n;return Module.mono_bind_static_method(r)}function m(e,t){return r(this,void 0,void 0,function(){var n,r;return o(this,function(o){switch(o.label){case 0:if("function"!=typeof WebAssembly.instantiateStreaming)return[3,4];o.label=1;case 1:return o.trys.push([1,3,,4]),[4,WebAssembly.instantiateStreaming(e.response,t)];case 2:return[2,o.sent().instance];case 3:return n=o.sent(),console.info("Streaming compilation failed. Falling back to ArrayBuffer instantiation. ",n),[3,4];case 4:return[4,e.response.then(function(e){return e.arrayBuffer()})];case 5:return r=o.sent(),[4,WebAssembly.instantiate(r,t)];case 6:return[2,o.sent().instance]}})})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=window.chrome&&navigator.userAgent.indexOf("Edge")<0,o=!1;function a(){return o&&r}t.hasDebuggingEnabled=a,t.attachDebuggerHotkey=function(e){var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";a()&&console.info("Debugging hotkey: Shift+"+t+"+D (when application has focus)"),o=!!e.bootConfig.resources.pdb,document.addEventListener("keydown",function(e){var t;e.shiftKey&&(e.metaKey||e.altKey)&&"KeyD"===e.code&&(o?r?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("Currently, only Edge(Chromium) or Chrome is supported for debugging."):console.error("Cannot start debugging, because the application was not compiled with debugging enabled."))})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(23),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=a,this.arrayBuilderSegmentReader=i,this.diffReader=u,this.editReader=s,this.frameReader=l}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,a.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*a.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*a.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return c(e,t,u.structLength)},e.prototype.referenceFramesEntry=function(e,t){return c(e,t,l.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=c(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=c(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var a={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},i={structLength:12,values:function(e){var t=r.platform.readObjectField(e,0),n=r.platform.getObjectFieldsBaseAddress(t);return r.platform.readObjectField(n,0)},offset:function(e){return r.platform.readInt32Field(e,4)},count:function(e){return r.platform.readInt32Field(e,8)}},u={structLength:4+i.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return c(e,t,s.structLength)}},s={structLength:20,editType:function(e){return r.platform.readInt32Field(e,0)},siblingIndex:function(e){return r.platform.readInt32Field(e,4)},newTreeIndex:function(e){return r.platform.readInt32Field(e,8)},moveToSiblingIndex:function(e){return r.platform.readInt32Field(e,8)},removedAttributeName:function(e){return r.platform.readStringField(e,16)}},l={structLength:36,frameType:function(e){return r.platform.readInt16Field(e,4)},subtreeLength:function(e){return r.platform.readInt32Field(e,8)},elementReferenceCaptureId:function(e){return r.platform.readStringField(e,16)},componentId:function(e){return r.platform.readInt32Field(e,12)},elementName:function(e){return r.platform.readStringField(e,16)},textContent:function(e){return r.platform.readStringField(e,16)},markupContent:function(e){return r.platform.readStringField(e,16)},attributeName:function(e){return r.platform.readStringField(e,16)},attributeValue:function(e){return r.platform.readStringField(e,24)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function c(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,a){function i(e){try{s(r.next(e))}catch(e){a(e)}}function u(e){try{s(r.throw(e))}catch(e){a(e)}}function s(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(i,u)}s((r=r.apply(e,t||[])).next())})},o=this&&this.__generator||function(e,t){var n,r,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:u(0),throw:u(1),return:u(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function u(a){return function(u){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(o=(o=i.trys).length>0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1] { window['Blazor'].start = boot; if (shouldAutoStart()) { boot().catch(error => { - Module.printErr(error); // Logs it, and causes the error UI to appear + if (typeof Module !== 'undefined' && Module.printErr) { + // Logs it, and causes the error UI to appear + Module.printErr(error); + } else { + // The error must have happened so early we didn't yet set up the error UI, so just log to console + console.error(error); + } }); } diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 43a434ddae..d1be3337da 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -24,7 +24,7 @@ export const monoPlatform: Platform = { // before we start loading the WebAssembly files addGlobalModuleScriptTagsToDocument(() => { window['Module'] = createEmscriptenModuleInstance(resourceLoader, resolve, reject); - addScriptTagsToDocument(); + addScriptTagsToDocument(resourceLoader); }); }); }, @@ -112,15 +112,29 @@ export const monoPlatform: Platform = { }, }; -function addScriptTagsToDocument() { +function addScriptTagsToDocument(resourceLoader: WebAssemblyResourceLoader) { const browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate; if (!browserSupportsNativeWebAssembly) { throw new Error('This browser does not support WebAssembly.'); } + // The dotnet.*.js file has a version or hash in its name as a form of cache-busting. This is needed + // because it's the only part of the loading process that can't use cache:'no-cache' (because it's + // not a 'fetch') and isn't controllable by the developer (so they can't put in their own cache-busting + // querystring). So, to find out the exact URL we have to search the boot manifest. + const dotnetJsResourceName = Object + .keys(resourceLoader.bootConfig.resources.runtime) + .filter(n => n.startsWith('dotnet.') && n.endsWith('.js'))[0]; const scriptElem = document.createElement('script'); - scriptElem.src = '_framework/wasm/dotnet.js'; + scriptElem.src = `_framework/wasm/${dotnetJsResourceName}`; scriptElem.defer = true; + + // For consistency with WebAssemblyResourceLoader, we only enforce SRI if caching is allowed + if (resourceLoader.bootConfig.cacheBootResources) { + const contentHash = resourceLoader.bootConfig.resources.runtime[dotnetJsResourceName]; + scriptElem.integrity = contentHash; + } + document.body.appendChild(scriptElem); } @@ -165,8 +179,8 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade const dotnetWasmResource = await resourceLoader.loadResource( /* name */ dotnetWasmResourceName, /* url */ `_framework/wasm/${dotnetWasmResourceName}`, - /* hash */ resourceLoader.bootConfig.resources.wasm[dotnetWasmResourceName]); - compiledInstance = await compileWasmModule(dotnetWasmResource, imports); + /* hash */ resourceLoader.bootConfig.resources.runtime[dotnetWasmResourceName]); + compiledInstance = await compileWasmModule(dotnetWasmResource, imports); } catch (ex) { module.printErr(ex); throw ex; diff --git a/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts b/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts index 1fa28ce239..91fa6da35e 100644 --- a/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts +++ b/src/Components/Web.JS/src/Platform/WebAssemblyResourceLoader.ts @@ -12,19 +12,13 @@ export class WebAssemblyResourceLoader { credentials: 'include', cache: networkFetchCacheMode }); + const bootConfig: BootJsonData = await bootConfigResponse.json(); + const cache = await getCacheToUseIfEnabled(bootConfig); - // Define a separate cache for each base href, so we're isolated from any other - // Blazor application running on the same origin. We need this so that we're free - // to purge from the cache anything we're not using and don't let it keep growing, - // since we don't want to be worst offenders for space usage. - const relativeBaseHref = document.baseURI.substring(document.location.origin.length); - const cacheName = `blazor-resources-${relativeBaseHref}`; - return new WebAssemblyResourceLoader( - await bootConfigResponse.json(), - await caches.open(cacheName)); + return new WebAssemblyResourceLoader(bootConfig, cache); } - constructor (public readonly bootConfig: BootJsonData, private cache: Cache) + constructor (public readonly bootConfig: BootJsonData, private cacheIfUsed: Cache | null) { } @@ -34,21 +28,15 @@ export class WebAssemblyResourceLoader { } loadResource(name: string, url: string, contentHash: string): LoadingResource { - // Setting 'cacheBootResources' to false bypasses the entire cache flow, including integrity checking. - // This gives developers an easy opt-out if they don't like anything about the default cache mechanism. + // Note that if cacheBootResources was explicitly disabled, we also bypass hash checking + // This is to give developers an easy opt-out from the entire caching/validation flow if + // there's anything they don't like about it. - // There's also a Chromium bug we need to work around here: the CacheStorage APIs say that when - // caches.open(name) returns a promise that succeeds, the value is meant to be a Cache instance. - // However, if the browser was launched with a --user-data-dir param that's "too long" in some sense, - // then even through the promise resolves as success, the value given is `undefined`. - // See https://stackoverflow.com/a/46626574. We're reporting this to Chromium and others, but in - // the meantime, if this.cache isn't set, just proceed without caching. - const useCache = this.bootConfig.cacheBootResources && this.cache; + const response = this.cacheIfUsed + ? this.loadResourceWithCaching(this.cacheIfUsed, name, url, contentHash) + : fetch(url, { cache: networkFetchCacheMode, integrity: this.bootConfig.cacheBootResources ? contentHash : undefined }); - const response = useCache - ? this.loadResourceWithCaching(name, url, contentHash) - : fetch(url, { cache: networkFetchCacheMode }); - return { name, url, response }; + return { name, url, response }; } logToConsole() { @@ -57,8 +45,12 @@ export class WebAssemblyResourceLoader { const cacheResponseBytes = countTotalBytes(cacheLoadsEntries); const networkResponseBytes = countTotalBytes(networkLoadsEntries); const totalResponseBytes = cacheResponseBytes + networkResponseBytes; - const linkerDisabledWarning = this.bootConfig.linkerEnabled ? '%c' : '\n%cThis application was built with linking (tree shaking) disabled. Published applications will be significantly smaller.'; + if (totalResponseBytes === 0) { + // We have no perf stats to display, likely because caching is not in use. + return; + } + const linkerDisabledWarning = this.bootConfig.linkerEnabled ? '%c' : '\n%cThis application was built with linking (tree shaking) disabled. Published applications will be significantly smaller.'; console.groupCollapsed(`%cblazor%c Loaded ${toDataSizeString(totalResponseBytes)} resources${linkerDisabledWarning}`, 'background: purple; color: white; padding: 1px 3px; border-radius: 3px;', 'font-weight: bold;', 'font-weight: normal;'); if (cacheLoadsEntries.length) { @@ -79,17 +71,20 @@ export class WebAssemblyResourceLoader { async purgeUnusedCacheEntriesAsync() { // We want to keep the cache small because, even though the browser will evict entries if it // gets too big, we don't want to be considered problematic by the end user viewing storage stats - const cachedRequests = await this.cache.keys(); - const deletionPromises = cachedRequests.map(async cachedRequest => { - if (!(cachedRequest.url in this.usedCacheKeys)) { - await this.cache.delete(cachedRequest); - } - }); + const cache = this.cacheIfUsed; + if (cache) { + const cachedRequests = await cache.keys(); + const deletionPromises = cachedRequests.map(async cachedRequest => { + if (!(cachedRequest.url in this.usedCacheKeys)) { + await cache.delete(cachedRequest); + } + }); - return Promise.all(deletionPromises); + await Promise.all(deletionPromises); + } } - private async loadResourceWithCaching(name: string, url: string, contentHash: string) { + private async loadResourceWithCaching(cache: Cache, name: string, url: string, contentHash: string) { // Since we are going to cache the response, we require there to be a content hash for integrity // checking. We don't want to cache bad responses. There should always be a hash, because the build // process generates this data. @@ -100,7 +95,7 @@ export class WebAssemblyResourceLoader { const cacheKey = toAbsoluteUri(`${url}.${contentHash}`); this.usedCacheKeys[cacheKey] = true; - const cachedResponse = await this.cache.match(cacheKey); + const cachedResponse = await cache.match(cacheKey); if (cachedResponse) { // It's in the cache. const responseBytes = parseInt(cachedResponse.headers.get('content-length') || '0'); @@ -109,12 +104,12 @@ export class WebAssemblyResourceLoader { } else { // It's not in the cache. Fetch from network. const networkResponse = await fetch(url, { cache: networkFetchCacheMode, integrity: contentHash }); - this.addToCacheAsync(name, cacheKey, networkResponse); // Don't await - add to cache in background + this.addToCacheAsync(cache, name, cacheKey, networkResponse); // Don't await - add to cache in background return networkResponse; } } - private async addToCacheAsync(name: string, cacheKey: string, response: Response) { + private async addToCacheAsync(cache: Cache, name: string, cacheKey: string, response: Response) { // We have to clone in order to put this in the cache *and* not prevent other code from // reading the original response stream. const responseData = await response.clone().arrayBuffer(); @@ -129,7 +124,7 @@ export class WebAssemblyResourceLoader { // Add to cache as a custom response object so we can track extra data such as responseBytes // We can't rely on the server sending content-length (ASP.NET Core doesn't by default) - await this.cache.put(cacheKey, new Response(responseData, { + await cache.put(cacheKey, new Response(responseData, { headers: { 'content-type': response.headers.get('content-type') || '', 'content-length': (responseBytes || response.headers.get('content-length') || '').toString() @@ -138,6 +133,34 @@ export class WebAssemblyResourceLoader { } } +async function getCacheToUseIfEnabled(bootConfig: BootJsonData): Promise { + // caches will be undefined if we're running on an insecure origin (secure means https or localhost) + if (!bootConfig.cacheBootResources || typeof caches === 'undefined') { + return null; + } + + // Define a separate cache for each base href, so we're isolated from any other + // Blazor application running on the same origin. We need this so that we're free + // to purge from the cache anything we're not using and don't let it keep growing, + // since we don't want to be worst offenders for space usage. + const relativeBaseHref = document.baseURI.substring(document.location.origin.length); + const cacheName = `blazor-resources-${relativeBaseHref}`; + + try { + // There's a Chromium bug we need to be aware of here: the CacheStorage APIs say that when + // caches.open(name) returns a promise that succeeds, the value is meant to be a Cache instance. + // However, if the browser was launched with a --user-data-dir param that's "too long" in some sense, + // then even through the promise resolves as success, the value given is `undefined`. + // See https://stackoverflow.com/a/46626574 and https://bugs.chromium.org/p/chromium/issues/detail?id=1054541 + // If we see this happening, return "null" to mean "proceed without caching". + return (await caches.open(cacheName)) || null; + } catch { + // There's no known scenario where we should get an exception here, but considering the + // Chromium bug above, let's tolerate it and treat as "proceed without caching". + return null; + } +} + function countTotalBytes(loads: LoadLogEntry[]) { return loads.reduce((prev, item) => prev + (item.responseBytes || 0), 0); } @@ -162,9 +185,9 @@ interface BootJsonData { } interface ResourceGroups { - readonly wasm: ResourceList; readonly assembly: ResourceList; readonly pdb?: ResourceList; + readonly runtime: ResourceList; } interface LoadLogEntry { diff --git a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs b/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs index 42325144e5..889d9de236 100644 --- a/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs +++ b/src/Components/WebAssembly/Build/src/Tasks/GenerateBlazorBootJson.cs @@ -110,7 +110,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { // If RelativeOutputPath was not specified, we assume the item will be placed at the // root of whatever directory is used for its resource type (e.g., assemblies go in _bin) - outputPath = Path.GetFileName(item.ItemSpec); + outputPath = Path.GetFileName(item.GetMetadata("TargetOutputPath")); } return outputPath.Replace('\\', '/'); @@ -159,7 +159,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { assembly, pdb, - wasm + runtime, } #pragma warning restore IDE1006 // Naming Styles } diff --git a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets index aa5fc890fe..d5da8dd960 100644 --- a/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets +++ b/src/Components/WebAssembly/Build/src/targets/Blazor.MonoRuntime.targets @@ -76,13 +76,15 @@ <_BlazorCopyLocalPaths Remove="@(_BlazorManagedRuntimeAssemby)" /> - true + <_BlazorBootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly + <_BlazorBootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb $(BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension) %(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension) - true + <_BlazorBootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly + <_BlazorBootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb $(BlazorRuntimeBinOutputPath)%(FileName)%(Extension) %(FileName)%(Extension) @@ -111,21 +113,41 @@ <_BlazorCopyLocalPaths Remove="@(_BlazorManagedRuntimeAssemby)" Condition="'%(Extension)' == '.dll'" /> - true + <_BlazorBootManifestResourceType Condition="'%(Extension)' == '.dll'">assembly + <_BlazorBootManifestResourceType Condition="'%(Extension)' == '.pdb'">pdb $(BlazorRuntimeBinOutputPath)%(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension) %(_BlazorCopyLocalPaths.DestinationSubDirectory)%(FileName)%(Extension) - - - - + + + 3.2.0-preview2 + + + + $(BlazorRuntimeWasmOutputPath)%(FileName)%(Extension) - true + <_BlazorBootManifestResourceType>runtime + + + + $(BlazorRuntimeWasmOutputPath)%(FileName).$(TemporaryDotNetJsFileVersion)%(Extension) + <_BlazorBootManifestResourceType>runtime + + + + $(BaseBlazorRuntimeOutputPath)%(FileName)%(Extension) @@ -300,13 +322,12 @@ Inputs="$(MSBuildAllProjects);@(BlazorOutputWithTargetPath)" Outputs="$(BlazorBootJsonIntermediateOutputPath)"> - <_BlazorBootResource Include="@(BlazorOutputWithTargetPath->WithMetadataValue('BlazorRuntimeFile', 'true'))" /> - <_BlazorBootResource BootResourceType="assembly" Condition="'%(Extension)' == '.dll'" /> - <_BlazorBootResource BootResourceType="pdb" Condition="'%(Extension)' == '.pdb'" /> - <_BlazorBootResource BootResourceType="wasm" Condition="'%(Extension)' == '.wasm'" /> + <_BlazorBootResource Include="@(BlazorOutputWithTargetPath->HasMetadata('_BlazorBootManifestResourceType'))"> + %(BlazorOutputWithTargetPath._BlazorBootManifestResourceType) + - + diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs index 567b15928e..fe3d57a6eb 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs +++ b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/BuildIntegrationTest.cs @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. @@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.boot.json"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "blazor.webassembly.js"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(buildOutputDirectory, "wwwroot", "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, buildOutputDirectory, "wwwroot", "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. } diff --git a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs index 0a78a3d6e4..ad57f6f3bc 100644 --- a/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs +++ b/src/Components/WebAssembly/Build/test/BuildIntegrationTests/PublishIntegrationTest.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(blazorPublishDirectory, "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. @@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(blazorPublishDirectory, "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. @@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(blazorPublishDirectory, "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. @@ -164,7 +164,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(blazorPublishDirectory, "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. @@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(blazorPublishDirectory, "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. @@ -254,7 +254,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.boot.json"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "blazor.webassembly.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.wasm"); - Assert.FileExists(result, blazorPublishDirectory, "_framework", "wasm", "dotnet.js"); + Assert.FileCountEquals(result, 1, Path.Combine(blazorPublishDirectory, "_framework", "wasm"), "dotnet.*.js"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "standalone.dll"); Assert.FileExists(result, blazorPublishDirectory, "_framework", "_bin", "Microsoft.Extensions.Logging.Abstractions.dll"); // Verify dependencies are part of the output. diff --git a/src/Components/WebAssembly/Build/test/GenerateBlazorBootJsonTest.cs b/src/Components/WebAssembly/Build/test/GenerateBlazorBootJsonTest.cs index 21e6d3c791..dee7b38958 100644 --- a/src/Components/WebAssembly/Build/test/GenerateBlazorBootJsonTest.cs +++ b/src/Components/WebAssembly/Build/test/GenerateBlazorBootJsonTest.cs @@ -41,10 +41,10 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build fileHash: "pdbhashpdbhashpdbhash"), CreateResourceTaskItem( - ResourceType.wasm, - itemSpec: "some-wasm-file", + ResourceType.runtime, + itemSpec: "some-runtime-file", relativeOutputPath: null, - fileHash: "wasmhashwasmhashwasmhash") + fileHash: "runtimehashruntimehash") } }; @@ -75,9 +75,9 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build resourceListKey => { var resources = parsedContent.resources[resourceListKey]; - Assert.Equal(ResourceType.wasm, resourceListKey); + Assert.Equal(ResourceType.runtime, resourceListKey); Assert.Single(resources); - Assert.Equal("sha256-wasmhashwasmhashwasmhash", resources["some-wasm-file"]); + Assert.Equal("sha256-runtimehashruntimehash", resources["some-runtime-file"]); }); } @@ -145,6 +145,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Build { var mock = new Mock(); mock.Setup(m => m.ItemSpec).Returns(itemSpec); + mock.Setup(m => m.GetMetadata("TargetOutputPath")).Returns(itemSpec); mock.Setup(m => m.GetMetadata("BootResourceType")).Returns(type.ToString()); mock.Setup(m => m.GetMetadata("RelativeOutputPath")).Returns(relativeOutputPath); mock.Setup(m => m.GetMetadata("FileHash")).Returns(fileHash); diff --git a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js b/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js index 328acacdff..75de60262b 100644 --- a/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js +++ b/src/Components/WebAssembly/testassets/MonoSanity/wwwroot/loader.js @@ -110,14 +110,18 @@ } } - function addScriptTagsToDocument() { + async function addScriptTagsToDocument() { var browserSupportsNativeWebAssembly = typeof WebAssembly !== 'undefined' && WebAssembly.validate; if (!browserSupportsNativeWebAssembly) { throw new Error('This browser does not support WebAssembly.'); } + var bootJson = await fetch('/_framework/blazor.boot.json').then(res => res.json()); + var dotNetJsResourceName = Object.keys(bootJson.resources.runtime) + .filter(name => name.endsWith('.js')); + var scriptElem = document.createElement('script'); - scriptElem.src = '/_framework/wasm/dotnet.js'; + scriptElem.src = '/_framework/wasm/' + dotNetJsResourceName; document.body.appendChild(scriptElem); }