@angular-devkit/build-angular
Version:
Angular Webpack Build Facade
185 lines (184 loc) • 6.91 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = default_1;
// eslint-disable-next-line import/no-extraneous-dependencies
const core_1 = require("@angular/core");
const rxjs_1 = require("rxjs");
function default_1(mod) {
if (!mod['hot']) {
return;
}
if (!(0, core_1.isDevMode)()) {
console.error(`[NG HMR] Cannot use HMR when Angular is running in production mode. To prevent production mode, do not call 'enableProdMode()'.`);
return;
}
mod['hot'].accept();
mod['hot'].dispose(() => {
if (typeof ng === 'undefined') {
console.warn(`[NG HMR] Cannot find global 'ng'. Likely this is caused because scripts optimization is enabled.`);
return;
}
if (!ng.getInjector) {
// View Engine
return;
}
// Reset JIT compiled components cache
(0, core_1.ɵresetCompiledComponents)();
const appRoot = getAppRoot();
if (!appRoot) {
return;
}
const appRef = getApplicationRef(appRoot);
if (!appRef) {
return;
}
// Inputs that are hidden should be ignored
const oldInputs = document.querySelectorAll('input:not([type="hidden"]), textarea');
const oldOptions = document.querySelectorAll('option');
// Create new application
appRef.components.forEach((cp) => {
const element = cp.location.nativeElement;
const parentNode = element.parentNode;
parentNode.insertBefore(document.createElement(element.tagName), element);
parentNode.removeChild(element);
});
// Destroy old application, injectors, <style..., etc..
const platformRef = getPlatformRef(appRoot);
if (platformRef) {
platformRef.destroy();
}
// Restore all inputs and options
const bodyElement = document.body;
if (oldInputs.length + oldOptions.length === 0 || !bodyElement) {
return;
}
// Use a `MutationObserver` to wait until the app-root element has been bootstrapped.
// ie: when the ng-version attribute is added.
new MutationObserver((_mutationsList, observer) => {
observer.disconnect();
const newAppRoot = getAppRoot();
if (!newAppRoot) {
return;
}
const newAppRef = getApplicationRef(newAppRoot);
if (!newAppRef) {
return;
}
// Wait until the application isStable to restore the form values
newAppRef.isStable
.pipe((0, rxjs_1.filter)((isStable) => !!isStable), (0, rxjs_1.take)(1))
.subscribe(() => restoreFormValues(oldInputs, oldOptions));
}).observe(bodyElement, {
attributes: true,
subtree: true,
attributeFilter: ['ng-version'],
});
});
}
function getAppRoot() {
const appRoot = document.querySelector('[ng-version]');
if (!appRoot) {
console.warn('[NG HMR] Cannot find the application root component.');
return undefined;
}
return appRoot;
}
function getToken(appRoot, token) {
return (typeof ng === 'object' && ng.getInjector(appRoot).get(token)) || undefined;
}
function getApplicationRef(appRoot) {
const appRef = getToken(appRoot, core_1.ApplicationRef);
if (!appRef) {
console.warn(`[NG HMR] Cannot get 'ApplicationRef'.`);
return undefined;
}
return appRef;
}
function getPlatformRef(appRoot) {
const platformRef = getToken(appRoot, core_1.PlatformRef);
if (!platformRef) {
console.warn(`[NG HMR] Cannot get 'PlatformRef'.`);
return undefined;
}
return platformRef;
}
function dispatchEvents(element) {
element.dispatchEvent(new Event('input', {
bubbles: true,
cancelable: true,
}));
element.blur();
element.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
}
function restoreFormValues(oldInputs, oldOptions) {
// Restore input that are not hidden
const newInputs = document.querySelectorAll('input:not([type="hidden"]), textarea');
if (newInputs.length && newInputs.length === oldInputs.length) {
console.log('[NG HMR] Restoring input/textarea values.');
for (let index = 0; index < newInputs.length; index++) {
const newElement = newInputs[index];
const oldElement = oldInputs[index];
switch (oldElement.type) {
case 'button':
case 'image':
case 'submit':
case 'reset':
// These types don't need any value change.
continue;
case 'radio':
case 'checkbox':
newElement.checked = oldElement.checked;
break;
case 'color':
case 'date':
case 'datetime-local':
case 'email':
case 'hidden':
case 'month':
case 'number':
case 'password':
case 'range':
case 'search':
case 'tel':
case 'text':
case 'textarea':
case 'time':
case 'url':
case 'week':
newElement.value = oldElement.value;
break;
case 'file':
// Ignored due: Uncaught DOMException: Failed to set the 'value' property on 'HTMLInputElement':
// This input element accepts a filename, which may only be programmatically set to the empty string.
break;
default:
console.warn('[NG HMR] Unknown input type ' + oldElement.type + '.');
continue;
}
dispatchEvents(newElement);
}
}
else if (oldInputs.length) {
console.warn('[NG HMR] Cannot restore input/textarea values.');
}
// Restore option
const newOptions = document.querySelectorAll('option');
if (newOptions.length && newOptions.length === oldOptions.length) {
console.log('[NG HMR] Restoring selected options.');
for (let index = 0; index < newOptions.length; index++) {
const newElement = newOptions[index];
newElement.selected = oldOptions[index].selected;
dispatchEvents(newElement);
}
}
else if (oldOptions.length) {
console.warn('[NG HMR] Cannot restore selected options.');
}
}
;