# Dive into NativeScript hooks
NativeScript hooks are executable pieces of code or Node.js scripts which could be added by application or plugin developers in order to customize the execution of particular nativescript commands. They give you the power to perform special activities by plugging in different parts of the build process of the application.
NativeScript CLI supports two different ways of executing hooks:
* in-process execution
* spawned execution via node.js's spawn function
`In-process` execution is available only for JavaScript hooks. NativeScript CLI determinates if the hook should be executed `in-process` based on the `module.exports` statement. So in order to enable `in-process` execution all you need to have is `module.exports = ...` statement in the hook. The `In-process` execution gives you the flexibility to use any available service from NativeScript CLI.
If NativeScript CLI cannot find `module.exports =` statement, it will execute the hook using Node.js [childProces.spawn](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) function. If a hook returns a non-zero exit code, then the nativescript command will throw an exception. Using this approach you're unable to use any of the services from NativeScript CLI.
> NOTE: We highly recommend writing `in-process` hooks using Node.js.
# In-process hooks
If you're writting `in-process` hooks you should use the following module definition:
```javascript=
module.exports = function() { ... }
```
## hookArgs
The above function can accept arguments. NativeScript CLI supports a special argument named `hookArgs`. The `hookArgs` is an object containing all the arguments passed to the hooked method. For example, let's say NativeScript CLI has a method `prepareJSApp` with the following definition:
```typescript=
@hook("prepareJSApp")
public async prepareJSApp(projectData: IProjectData, platformData: IPlatformData) { }
```
Then,`hookArgs` will have the following structure:
```json=
hookArgs: {
projectData,
platformData
}
```
```javascript=
module.exports = function(hookArgs) {
console.log(hookArgs.projectData);
}
```
## Injected arguments
NativeScript CLI is constructed by Dependency Injection and executes `in-process` hooks in a such a way that allow you to use any of the registered services available in the injector. A full list of all registered services can be found [here](https://github.com/NativeScript/nativescript-cli/blob/master/lib/bootstrap.ts).
There are two approaches how services can be injected from whitin hooks:
1. You can inject and interact with any service of NativeScript CLI using the syntax below:
```javascript=
module.exports = function($logger, $fs, $projectDataService, hookArgs) {
$logger.info("Exectuing hook");
}
```
2. You can inject `$injector` and resolve any service from it.
```javascript=
module.exports = function($injector, hookArgs) {
const $logger = $injector.resolve("$logger");
const $fs = $injector.resolve("$fs");
}
```
The injected dependencies are resolved by name which means that if you inject for example `$logger1` by mistake, NativeScript CLI won't execute the hook and will show a warning:
*<hookName> will NOT be executed because it has invalid arguments - $logger1*
> NOTE: When the second approach is used and services are resolved directly from `$injector`, NativeScript CLI will NOT show a warning. That means if you resolve `$logger1` by mistake an error will be thrown during the hook's execution.
# Spawned hooks
The spawned hooks are run via Node child_process spawn from the project's root directory. All options are passed to the script using environment variables:
| The name of environment variable | Description |
| -------- | -------- |
| TNS-VERSION | The version of the NativeScript CLI. |
| TNS-HOOK_FULL_PATH | The full path to the executed hook. |
| TNS-COMMANDLINE | The exact command-line arguments passed to NativeScript CLI (e.g: tns run ios --emulator). |
If a script returns a non-zero exit code, then NativeScript CLI will throw an error and command's execution will be aborted.
# Traditional via replacement hooks
The hooks mechanism in NativeScript CLI supports the followings:
* Execute code before or after some specific action
* Replace concrete action
For example, you may want to check if a release option is provided in order to change the API keys of third party libraries. In such a case, you could use before/after hook:
```javascript=
module.exports = function(hookArgs) {
if (hookArgs.prepareData.release) {
console.log("Before executing release build.");
}
}
```
The hook below will also be executed as before/after hook:
```javascript=
module.exports = function(hookArgs, $logger) {
return () => {
$logger.info("Replaced the original CLI function.")
}
}
```
The last hook will be executed as a replacement hook. NativeScript CLI will see it returns a function, so it will just replace the actual executed function from CLI with it.
> NOTE: We strongly recommend using the hooks in the first manner. The replacement hooks should be used only in specific rare cases.
# Async code from hooks
NativeScript CLI supports async code from hooks. If you execute async code in your hook, you need to return a promise, otherwise execution will continue before your hook completes:
```javascript=
var mkdirp = require('mkdirp');
module.exports = function($logger) {
return new Promise(function(resolve, reject) {
mkdirp('somedir', function(err) {
if (err) {
reject(err);
else {
resolve();
}
})
});
}
```
# How can I add hooks to my plugin
In order to add hooks you simple have to do the following easy steps:
1. Install `nativescript-hook` as a dependency to your plugin `npm i nativescript-hook --save`
2. Create `postinstall.js` file at the root folder of your plugin with the following content:
```javascript=
var hook = require("nativescript-hook")(__dirname);
hook.postinstall();
```
3. Create `preuninstall.js` file at the root folder of your plugin with the following content:
```javascript=
var hook = require("nativescript-hook")(__dirname);
hook.preuninstall();
```
4. Add `postinstall` and `preuninstall` scripts to your package.json
```json=
"scripts": {
"postinstall": "node postinstall.js",
"preuninstall": "node preuninstall.js"
}
```
5. Declare your `hooks` under the `nativescript` property in your `package.json` file
```json=
"nativescript": {
"hooks": [
{
"type": "before-prepare",
"script": "hooks/before-prepare.js",
"inject": true
}
]
},
```
### Type property
The `type` property specifies the type of the hook. NativeScript CLI supports `before` or `after` hooks with the following types:
| Hook type | Description |
| -------- | -------- |
| prepare | It is executed `before/after` the actual preparation of application. |
| prepareJSApp | It is executed `before/after` the javascript preparation of the application. |
| shouldPrepare | It is executed `before/after` NativeScript had decided if the application should be prepared. This allow you to control if the application will be prepared as NativeScript CLI prepares the application only when `shouldPrepare` returns `true`. |
| checkForChanges | It is executed `before/after` NativeScript checks if there are changes in your application. NativeScript CLI keeps a state of your application and based on this state decides if the application should be rebuilt, reinstalled or restarted on device. |
| liveSync | It is executed `before/after` the initial sync of the application. |
| watch | It is executed `before/after` NativeScript CLI starts/stops the watchers on the application. |
| watchPatterns | Text |
| buildAndroidPlugin | Text |
| install | Text |
| cleanApp | Text |
| before-preview-sync | Text |
| after-create | Text |
### Script property
The script property is the relative path from the root of the plugin to the file where the implementation of the hook is located.
### Inject property
The inject property is a boolean property that indicates if the hook will be executed `in-process` or will be spawned. The hook will be executed `in-process` when `inject` is `true`.
# How can I add hooks to my application
# The structure of hooks folder