Great Webpack HMR Learning Guide with Source Analysis

Learning time: 2020.06.14
Learning Chapters: Webpack HMR Principle Analysis

1. Introduction to HMR

Hot Module Replacement (hereinafter referred to as Hot Replacement for HMR modules) is a very useful feature provided by Webpack that allows various modules to be updated while JavaScript is running without having to refresh completely.

Hot Module Replacement (or HMR) is one of the most useful features offered by webpack. It allows all kinds of modules to be updated at runtime without the need for a full refresh.
--<Hot Module Replacement>

When we modify the code and save it, Webpack repackages it, and HMR replaces, adds, or deletes modules while the application is running without reloading the entire page.
HMR significantly accelerates development in the following ways:

  • Retain the application state that was lost when the page was fully reloaded;
  • Only update changes to save valuable development time;
  • Adjusting styles is faster - almost equivalent to changing styles in the browser debugger.

It is important to note that HMR is not suitable for production environments, which means it should only be used in development environments.

2. Usage of HMR

Enabling HMR functionality in a Webpack is relatively simple:

1. Mode 1: Use devServer

1.1 Setting devServer options

Just need toWebpack.config.jsAdd the devServer option, set the hot value to true, and use HotModuleReplacementPlugin and NamedModulesPlugin (optional) two Plugins:

// webpack.config.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.join(__dirname, '/')
    },
+    devServer: {
+        hot: true,   // Startup Module Hot Update HMR
+   open: true,  // Open automatic browser page
+    },
  plugins: [
+   new webpack.NamedModulesPlugin(),
+   new webpack.HotModuleReplacementPlugin()
  ]
}

1.2 Add scripts

And thenPackage.jsonThe scripts command in

// package.json

{
  // ...
  "scripts": {
+    "start": "webpack-dev-server"
  },
  // ...
}

2. Mode 2. Using command line parameters

The other is by adding the--hot parameter.After adding the --hot parameter, devServer tells Webpack to automatically introduce HotModuleReplacementPlugin instead of manually.
It's also often paired with--open to automatically open the browser to the page.
Remove the two Plugins you added earlier here:

// webpack.config.js

const path = require('path')
const webpack = require('webpack')
module.exports = {
    // ...
- plugins: [
-   new webpack.NamedModulesPlugin(),
-   new webpack.HotModuleReplacementPlugin()
- ]
}

Then modifyPackage.jsonThe scripts configuration in the file:

// package.json

{
  // ...
  "scripts": {
-    "start": "webpack-dev-server"
+    "start": "webpack-dev-server --hot --open"
  },
  // ...
}

3. Simple examples

Based on the above configuration, we simply implement a scenario:Index.jsImport in FileHello.jsModule, whenHello.jsWhen the module changes,Index.jsThe module will be updated.

The module code is implemented as follows:

// hello.js
export default () => 'hi leo!';

// index.js
import hello from './hello.js'
const div = document.createElement('div');
div.innerHTML = hello();

document.body.appendChild(div);

And thenIndex.htmlImport the packaged JS file in and execute the npm start run project:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <div>Great Webpack HMR Learning Guide</div>
    <script src="bundle.js"></script>
</body>
</html>

4. Implement monitoring updates

When we enable HMR through the HotModuleReplacementPlugin plug-in, its interfaces will be exposed globallyModule.hotUnder Properties.Typically, you can check if this interface is accessible before you start using it.
For example, you can accept an updated module like this:

if (module.hot) {
  module.hot.accept('./library.js', function() {
    // Use the updated library module to perform certain actions...
  })
}

aboutModule.hotMore API s to view official documentation <Hot Module Replacement API> .

Returning to the example above, we test the functionality of the update module.
At this point we will modifyIndex.jsCode to listenHello.jsUpdates in modules:

import hello from './hello.js';
const div = document.createElement('div');
div.innerHTML = hello();
document.body.appendChild(div);

+ if (module.hot) {
+   module.hot.accept('./hello.js', function() {
+     console.log('Updating now hello Module~');
+     div.innerHTML = hello();
+   })
+ }

Then modifyHello.jsFile content, test effect:

- export default () => 'hi leo!';
+ export default () => 'hi leo! hello world';

When we saved the code, the console output "Now updating the hello module~", and the page "hi leo!" is also updated to "hi leo! hello world", proving that we listened for file updates.



Simple Webpack HMR usage is described here, more, and read the official documentation <Hot Module Replacement>.

5. Common configuration and tips for devServer

5.1 Common Configurations

Depending on the directory structure, the contentBase, openPage parameters should be configured with appropriate values, otherwise the runtime should not immediately access your home page.Also be aware of your publicPath, the path generated by packaging static resources is a thought-provoking point, depending on your directory structure.

devServer: {
  contentBase: path.join(__dirname, 'static'),    // Tell the server where to provide content (default current working directory)
  openPage: 'views/index.html',  // Specify the page that opens when the browser starts by default
  index: 'views/index.html',  // Specify the location of the first page
  watchContentBase: true, // File changes under contentBase will reload the page (default false)
  host: 'localhost', // Default localhost, use'0.0.0.0'for external access
  port: 8080, // Default 8080
  inline: true, // Can monitor js changes
  hot: true, // Hot start
  open: true, // Automatically open the browser at startup (specify to open chrome, open:'Google Chrome')
  compress: true, // gzip compression is enabled for all services
  disableHostCheck: true, // true: no host ing check
  quiet: false,
  https: false,
  clientLogLevel: 'none',
  stats: { // Set the prompt for the console
    chunks: false,
    children: false,
    modules: false,
    entrypoints: false, // Whether to output entry information
    warnings: false,
    performance: false, // Whether to output webpack recommendations (such as file size)
  },
  historyApiFallback: {
    disableDotRule: true,
  },
  watchOptions: {
    ignored: /node_modules/, // Skip node_modules directory
  },
  proxy: { // Interface proxy (this configuration is more recommended: write toPackage.jsonAnd bring it here)
    "/api-dev": {
      "target": "http://api.test.xxx.com",
      "secure": false,
      "changeOrigin": true,
      "pathRewrite": { // Rewrite a paragraph on the url (for example, replace api-dev with empty here)
        "^/api-dev": ""
      }
    }
  },
  before(app) { },
}

5.2 Tip 1: Output dev-server code as a file

The dev-server output code is usually in memory, but it can also be written to the hard disk, producing entity files:

devServer:{
  writeToDisk: true,
}

Typically, it can be used for debugging proxy mapping files, which produce many js files with hash at compile time. Files without hash are also compiled in real time.

5.3 Tip 2: Start services using local IP by default

Sometimes when you start a service, you want to open it by default with a local ip address:

devServer:{
  disableHostCheck: true, // true: no host ing check
  // useLocalIp: true, //Recommended not configured here
  // Host:'0.0.0.0', //Recommended not configured here
}

You also need to configure host as 0.0.0.0, which is recommended to append to the scripts command instead of writing to death in the configuration. Otherwise, you don't want to go back and forth this way in the future, just to make a mistake, with a new command:

"dev-ip": "yarn run dev --host 0.0.0.0 --useLocalIp"

5.4 Tip 3: Specify the debug domain name to start

Sometimes you want to start with the specified debug domain name, for example:local.test.baidu.com:

devServer:{
  open: true,
  public: 'local.test.baidu.com:8080', // Port is required
  port: 8080,
}

At the same time, 127.0.0.1 needs to be modified to the specified host, which can be modified with tools such as iHost. Each tool is similar but has the following format:

127.0.0.1 local.test.baidu.com

The service will open automatically when it startsLocal.test.baidu.Com:8080Access

5.5 Tip 4: Start gzip compression

devServer:{
  compress: true,
}

3. Introduction of basic principles of HMR

From the previous introduction, we know that the main function of HMR is to replace, add, or delete modules while the application is running without reloading the entire page.
So how do the file changes resulting from Webpack compiling source code relate at compile time to the implementation of the replacement module at run time?


With these two questions in mind, let's take a brief look at the core HMR workflow (simplified version):

Next, start the HMR workflow analysis:

  1. When the Webpack (Watchman) listens for changes in the file/module code in the project, it notifies the construction tool (Packager) in the Webpack, HMR Plugin, of the changes;
  2. After HMR Plugin processing, the results are sent to the Application's runtime framework (HMR Runtime);
  3. These changed files/modules are finally updated (added/deleted or replaced) by the HMR Runtime into the module system.

Among them, HMR Runtime is injected at compile time by the build tool, which corresponds to the compile-time file and the run-time module through a uniform Module ID, and provides a series of API s to supply application-tier frameworks (such as React) calls.

_Be careful_: It is recommended that you first understand the general process of the diagram above and read it later.Don't worry, I'm waiting for you~ 😃

4. HMR Completeness Principle and Source Code Analysis

From the previous section, we know about HMR's simple workflow, so perhaps you still have a lot of confusion right now: What informs HMR Plugin about file updates?How does HMR Plugin send updates to HMR Runtime?And so on.

Then let's start to analyze the whole HMR module hot update process in detail with the source code. First, look at the flowchart, without knowing the method name in the diagram (red font yellow background color part):

The figure above shows a complete HMR workflow from our code modification to the hot update of the module, which is identified by a red Arabic numeric symbol.

To understand how this works, let's first understand these name concepts in the diagram:

  • Webpack-dev-server : A server plug-in, equivalent to an express server, starts a Web service and is only suitable for development environments;
  • Webpack-dev-middleware A Webpack-dev-server middleware that simply summarizes its functions as follows: through watch mode, listen for changes in resources, and then package automatically.
  • Webpack-hot-middleware : Combined with the middleware used by Webpack-dev-middleware, it can make the browser refreshless update, which is also called HMR;

Here's how HMR works:

1. Monitor code changes, recompile packages

First, according to devServer configuration, use npm start to start Webpack-dev-server to start the local server and enter the watch mode of Webpack, then initialize Webpack-dev-middleware to watch the file system by calling startWatch() in Webpack-dev-middleware:

// webpack-dev-server\bin\webpack-dev-server.js
// 1. Start the local server Line 386
server = new Server(compiler, options);

// webpack-dev-server\lib\Server.js
// 2. Initialize Webpack-dev-middleware Line 109
this.middleware = webpackDevMiddleware(compiler, Object.assign({}, options, wdmOptions));

// webpack-dev-middleware\lib\Shared.js
// 3. Start watch file system Line 171
startWatch: function() {
    //...
    // start watching
    if(!options.lazy) {
        var watching = compiler.watch(options.watchOptions, share.handleCompilerCallback);
        context.watching = watching;
    }
    //...
}
share.startWatch();
// ...

When the startWatch() method executes, it enters watch mode, and if changes are found in the code in the file, the module is recompiled and packaged according to the configuration file.

2. Save compiled results

Webpack interacts with Webpack-dev-middleware, which calls Webpack's API to monitor code changes and notifies Webpack to save recompiled code in memory through JavaScript objects.

We will find that inOutput.pathThe specified dist directory does not have a file to save the compilation results. Why?

In fact, Webpack keeps the compiled results in memory, because accessing code in memory is faster than accessing files in the file system, which reduces the cost of writing code to files.

Webpack can save code to memory, thanks to Webpack-dev-middleware's memory-fs dependency library, which replaces the original output FileSystem with an instance of MemoryFileSystem to output code to memory.Some of the sources are as follows:

// webpack-dev-middleware\lib\Shared.js Line 108

// store our files in memory
var fs;
var isMemoryFs = !compiler.compilers && 
    compiler.outputFileSystem instanceof MemoryFileSystem;
if(isMemoryFs) {
    fs = compiler.outputFileSystem;
} else {
    fs = compiler.outputFileSystem = new MemoryFileSystem();
}
context.fs = fs;

The code above first determines if the fileSystem is an instance of MemoryFileSystem, or if not, replaces the output FileSystem before the compiler with an instance of MemoryFileSystem.suchBundle.jsThe file code is stored in memory as a simple JavaScript object when requested by the browserBundle.jsWhen the file is in use, devServer goes directly to memory to find the JavaScript object saved above and returns it to the browser.

3. Monitor file changes and refresh browser

Webpack-dev-server starts monitoring file changes, and unlike step 1, this is not a recompile package that monitors code changes.
When we configure the devServer.watchContentBase To true, Webpack-dev-server monitors changes in static files in the configuration folder and notifies the browser side to refresh the application when changes occur, unlike HMR.

// webpack-dev-server\lib\Server.js
// 1. Read parameter Line 385
if (options.watchContentBase) { defaultFeatures.push('watchContentBase'); }

// 2. Definition_watch method Line 697
Server.prototype._watch = function (watchPath) {
    // ...
  const watcher = chokidar.watch(watchPath, options).on('change', () => {
    this.sockWrite(this.sockets, 'content-changed');
  });

  this.contentBaseWatchers.push(watcher);
};

// 3. Execution_watch() listens for file changes Line 339
watchContentBase: () => {
    if (/^(https?:)?\/\//.test(contentBase) || typeof contentBase === 'number') {
        throw new Error('Watching remote files is not supported.');
    } else if (Array.isArray(contentBase)) {
        contentBase.forEach((item) => {
            this._watch(item);
        });
    } else {
        this._watch(contentBase);
    }
}

4. Establish WS to synchronize compile-phase state

This step is handled in Webpack-dev-server, mainly through sockjs (Webpack-dev-server dependency), establish a long WebSocket connection between the browser side (Client) and the server side (Webpack-dev-middleware) of Webpack-dev-server.

Then synchronize the status information of each stage of Webpack compilation and packaging to the browser side.There are two important steps:

  • Send status

Webpack-dev-server listens for done events of compiles through the Webpack API, and when compiles are completed, Webpack-dev-server passes through _The sendStats method sends the hash value of the compiled new module to the browser side with a socket.

  • Save State

The browser side will _The hash sent by sendStats is saved and will use the hot update of the post module.

// webpack-dev-server\lib\Server.js

// 1. Definition_sendStats method Line 685
// send stats to a socket or multiple sockets
Server.prototype._sendStats = function (sockets, stats, force) {
  //...
  this.sockWrite(sockets, 'hash', stats.hash);
};

// 2. Listen for done Event Line 86
compiler.plugin('done', (stats) => {
      // Hash value of the latest packaged file (stats.hash) passed in as a parameter _sendStats()
    this._sendStats(this.sockets, stats.toJson(clientStats));
    this._stats = stats;
});

// webpack-dev-server\client\index.js
// 3. Save hash value Line 74
var onSocketMsg = {
  // ...
  hash: function hash(_hash) {
    currentHash = _hash;
  },
  // ...
}
socket(socketUrl, onSocketMsg);

5. Publish messages on browser side

When the hash message is sent, the socket also sends an ok message to the Webpack-dev-server, and since the client (Client) does not request hot update codes or perform hot update module operations, it forwards the work back to the Webpack by emit ting a "webpackHotUpdate" message.

// webpack-dev-server\client\index.js
// 1. Processing ok message Line 135
var onSocketMsg = {
  // ...
  ok: function ok() {
      sendMsg('Ok');
      if (useWarningOverlay || useErrorOverlay) overlay.clear();
      if (initial) return initial = false; // eslint-disable-line no-return-assign
      reloadApp();
  },
  // ...
}

// 2. Processing Refresh APP Line 218
function reloadApp() {
  // ...
  if (_hot) {
    // Dynamic Loading emitter
    var hotEmitter = require('webpack/hot/emitter');
    hotEmitter.emit('webpackHotUpdate', currentHash);
    if (typeof self !== 'undefined' && self.window) {
      // broadcast update to window
      self.postMessage('webpackHotUpdate' + currentHash, '*');
    }
  }
  // ...
}

6. Pass hash to HMR

Webpack/hot/dev-server listens for browser-side webpackHotUpdate messages and passes the hash value of the new module to the client HMR core hub HotModuleReplacement.runtime And call the check method to detect the updates to determine whether they are browser refreshes or module hot updates.
If the browser refreshes, there is no next step.

// webpack\hot\dev-server.js
// 1. Listen for webpackHotUpdate Line 42
var hotEmitter = require("./emitter");
hotEmitter.on("webpackHotUpdate", function(currentHash) {
    lastHash = currentHash;
    if(!upToDate() && module.hot.status() === "idle") {
        log("info", "[HMR] Checking for updates on the server...");
        check();
    }
});

var check = function check() {
    module.hot.check(true).then(function(updatedModules) {
        if(!updatedModules) {
               // ...
                        window.location.reload();// Browser Refresh
            return;
        }
        if(!upToDate()) {
            check();
        }
    }).catch(function(err) { /*...*/});
};

// webpack\lib\HotModuleReplacement.runtime.js
// 3. Call HotModuleReplacement.runtime Defined check method Line 167
function hotCheck(apply) {
    if(hotStatus !== "idle") throw new Error("check() is only allowed in idle status");
    hotApplyOnUpdate = apply;
    hotSetStatus("check");
    return hotDownloadManifest(hotRequestTimeout).then(function(update) {
                //...
    });
}

7. Check for updates

When HotModuleReplacement.runtime When the check method is called, it is called JsonpMainTemplate.runtime The two methods, hotDownload UpdateChunk and hotDownload Manifest, in Get the latest module code, which have their source code expanded in the next step.

// webpack\lib\HotModuleReplacement.runtime.js
// 1. Call HotModuleReplacement.runtime Define the hotDownloadUpdateChunk method Line 171
function hotCheck(apply) {
    if(hotStatus !== "idle") throw new Error("check() is only allowed in idle status");
    hotApplyOnUpdate = apply;
    hotSetStatus("check");
    return hotDownloadManifest(hotRequestTimeout).then(function(update) {
                //...
        {
          // The hotDownloadUpdateChunk method calls the hotDownloadUpdateChunk
          hotEnsureUpdateChunk(chunkId);
        }
    });
}

Where hotEnsureUpdateChunk method calls hotDownloadUpdateChunk:

// webpack\lib\HotModuleReplacement.runtime.js Line 215
    function hotEnsureUpdateChunk(chunkId) {
        if(!hotAvailableFilesMap[chunkId]) {
            hotWaitingFilesMap[chunkId] = true;
        } else {
            hotRequestedFilesMap[chunkId] = true;
            hotWaitingFiles++;
            hotDownloadUpdateChunk(chunkId);
        }
    }

8. Request to update the latest file list

When the check method is called, it is called first JsonpMainTemplate.runtime The hotDownloadManifest method in gets if there is an update file by issuing an AJAX request to the server and returns mainfest to the browser if there is one.

This involves some native XMLHttpRequest, not all of them are pasted out~

// webpack\lib\JsonpMainTemplate.runtime.js
// HotDownload Manifest Definition Line 22
function hotDownloadManifest(requestTimeout) {
    return new Promise(function(resolve, reject) {
        try {
            var request = new XMLHttpRequest();
            var requestPath = $require$.p + $hotMainFilename$;
            request.open("GET", requestPath, true);
            request.timeout = requestTimeout;
            request.send(null);
        } catch(err) {
            return reject(err);
        }
        request.onreadystatechange = function() {
            // ...
        };
    });
}

9. Request to update the latest module code

In the hotDownloadManifest method, the hotDownloadUpdateChunk method is also executed, the latest module code is requested through JSONP, and the code is returned to the HMR runtime.

The HMR runtime then proceeds with the new code to determine whether it is a browser refresh or a module hot update.

// webpack\lib\JsonpMainTemplate.runtime.js
// HotDownload Manifest Definition Line 12
function hotDownloadUpdateChunk(chunkId) {
        // Create script tags to initiate JSONP requests
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.charset = "utf-8";
    script.src = $require$.p + $hotChunkFilename$;
    $crossOriginLoading$;
    head.appendChild(script);
}

10. Update Modules and Dependent References

This step is the core step of the entire module hot update (HMR), removing out-of-date modules and code through the HMR runtime's hotApply method, and adding new modules and code for hot update.

As you can see from the hotApply method, module hot replacement is mainly divided into three stages:

  1. Find out outdatedModules and outdatedDependencies for expiration dependencies;
// webpack\lib\HotModuleReplacement.runtime.js
// Find outdatedModules and outdatedDependencies Line 342
function hotApply() { 
  // ...
  var outdatedDependencies = {};
  var outdatedModules = [];
  function getAffectedStuff(updateModuleId) {
    var outdatedModules = [updateModuleId];
    var outdatedDependencies = {};
    // ...
    return {
        type: "accepted",
        moduleId: updateModuleId,
        outdatedModules: outdatedModules,
        outdatedDependencies: outdatedDependencies
    };
    };
  function addAllToSet(a, b) {
      for (var i = 0; i < b.length; i++) {
          var item = b[i];
          if (a.indexOf(item) < 0)
              a.push(item);
      }
  }
  for(var id in hotUpdate) {
      if(Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
          // ...omit redundant code
          if(hotUpdate[id]) {
              result = getAffectedStuff(moduleId);
          }
          if(doApply) {
              for(moduleId in result.outdatedDependencies) {
                 // Add to outdatedDependencies
                  addAllToSet(outdatedDependencies[moduleId], result.outdatedDependencies[moduleId]);
              }
          }
          if(doDispose) {
              // Add to outdatedModules
              addAllToSet(outdatedModules, [result.moduleId]);
              appliedUpdate[moduleId] = warnUnexpectedRequire;
          }
      }
  }
}
  1. Remove references to expired modules, dependencies, and all child elements from the cache;
// webpack\lib\HotModuleReplacement.runtime.js
// Remove the reference Line 442 for expired modules, dependencies, and all child elements from the cache
function hotApply() {
         // ...
    var idx;
    var queue = outdatedModules.slice();
    while(queue.length > 0) {
        moduleId = queue.pop();
        module = installedModules[moduleId];
        // ...
        // Remove module from cache
        delete installedModules[moduleId];
        // Remove unnecessary handling of expired dependencies
        delete outdatedDependencies[moduleId];
        // Remove references to all child elements
        for(j = 0; j < module.children.length; j++) {
            var child = installedModules[module.children[j]];
            if(!child) continue;
            idx = child.parents.indexOf(moduleId);
            if(idx >= 0) {
                child.parents.splice(idx, 1);
            }
        }
    } 
        // Remove obsolete dependencies from module subcomponents
        var dependency;
        var moduleOutdatedDependencies;
        for(moduleId in outdatedDependencies) {
            if(Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)) {
                module = installedModules[moduleId];
                if(module) {
                    moduleOutdatedDependencies = outdatedDependencies[moduleId];
                    for(j = 0; j < moduleOutdatedDependencies.length; j++) {
                        dependency = moduleOutdatedDependencies[j];
                        idx = module.children.indexOf(dependency);
                        if(idx >= 0) module.children.splice(idx, 1);
                    }
                }
            }
        }
}
  1. Add new module code to modules when u is next calledWebpack_Require_uWhen you use the (webpack override require method) method, you get new module code.
// webpack\lib\HotModuleReplacement.runtime.js
// Add new module code to Line 501 in modules
function hotApply() {
         // ...
    for(moduleId in appliedUpdate) {
        if(Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
            modules[moduleId] = appliedUpdate[moduleId];
        }
    }
}

After the hotApply method executes, the new code has replaced the old code, but our business code is not aware of these changes, so we need to notify the application layer through the accept event to use the new module for a "partial refresh", as we do in our business:


if (module.hot) {
  module.hot.accept('./library.js', function() {
    // Use the updated library module to perform certain actions...
  })
}

11. Hot Update Error Handling

During the hot update process, if abort or fail errors may occur during the hotApply process, the hot update falls back to the Browser Reload, and the hot update for the entire module is complete.

// webpack\hot\dev-server.js Line 13
module.hot.check(true).then(function (updatedModules) {
    if (!updatedModules) {
        return window.location.reload();
    }
    // ...
}).catch(function (err) {
    var status = module.hot.status();
    if (["abort", "fail"].indexOf(status) >= 0) {
        window.location.reload();
    }
});

V. Summary

This article mainly shares with you the use and implementation principle and source code analysis of Webpack. In source code analysis, through a "Webpack HMR working principle analysis" diagram to let you know the entire HMR workflow, HMR itself has a lot of source code content, many details are not completely written out in this paper, you need readers to slowly read and understand the source code.

Reference Article

1. Official Documents <Hot Module Replacement>
2.Webpack HMR Principle Analysis
3.<webpack HMR> 
4.Configure dev-server 

Tags: Javascript Webpack hot update socket

Posted on Tue, 16 Jun 2020 12:15:01 -0400 by YourNameHere