Skip to content

Commit 55b18e3

Browse files
committed
feat:hooks
1 parent 738577a commit 55b18e3

File tree

1 file changed

+112
-79
lines changed

1 file changed

+112
-79
lines changed

content/guide/nativescript-hooks-guide.md

Lines changed: 112 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ description: A Guide to using CLI hooks.
44
contributors:
55
- jcassidyav
66
---
7+
78
## Overview
89

910
NativeScript hooks are executable pieces of code or Node.js scripts that can be added by application or plugin developers to customize the execution of particular NativeScript commands. They provide the power to perform special activities by plugging into different parts of the build process of your application.
1011

11-
Hooks are added to the `hooks/` folder of a project by plugins, or are specified in the `nativescript.config.ts` by an application.
12+
Hooks are added to the `hooks/` folder of a project by plugins, or are specified in the `nativescript.config.ts` by an application.
1213

1314
For example, when `ns prepare ...` is executed, all script files in the `hooks/before-prepare/` and `hooks/after-prepare/` folders are executed as well.
1415

@@ -21,13 +22,15 @@ For versions of the NativeScript CLI < 9.0 hooks must be written in CommonJS wit
2122
The NativeScript CLI supports two different ways of executing hooks:
2223

2324
**1. In-Process Execution**
25+
2426
- Available only for JavaScript hooks
2527
- Executes within the CLI process
2628
- Provides access to NativeScript CLI services via dependency injection
2729
- Determined by the presence of `module.exports` statement
2830
- **Recommended approach** for writing hooks
2931

3032
**2. Spawned Execution**
33+
3134
- Executed via Node.js's `child_process.spawn` function
3235
- Run from the project's root directory
3336
- Cannot access NativeScript CLI services
@@ -40,7 +43,7 @@ The NativeScript CLI supports two different ways of executing hooks:
4043
To write an in-process hook, use the following module definition:
4144

4245
```javascript
43-
module.exports = function() {
46+
module.exports = function () {
4447
// Hook implementation
4548
}
4649
```
@@ -70,36 +73,37 @@ Then `hookArgs` will have the following structure:
7073
**Using hookArgs in your hook:**
7174

7275
```javascript
73-
module.exports = function(hookArgs) {
74-
console.log(hookArgs.projectData);
76+
module.exports = function (hookArgs) {
77+
console.log(hookArgs.projectData)
7578
}
7679
```
7780

7881
**Dependency Injection**
7982

8083
NativeScript CLI is built with Dependency Injection and executes in-process hooks in a way that allows you to use any registered service from the injector.
8184

82-
***Approach 1: Direct Service Injection***
85+
**_Approach 1: Direct Service Injection_**
8386

8487
```javascript
85-
module.exports = function($logger, $fs, $projectDataService, hookArgs) {
86-
$logger.info("Executing hook");
88+
module.exports = function ($logger, $fs, $projectDataService, hookArgs) {
89+
$logger.info('Executing hook')
8790
// Use $fs, $projectDataService, etc.
8891
}
8992
```
9093

91-
***Approach 2: Injector Resolution***
94+
**_Approach 2: Injector Resolution_**
9295

9396
```javascript
94-
module.exports = function($injector, hookArgs) {
95-
const $logger = $injector.resolve("$logger");
96-
const $fs = $injector.resolve("$fs");
97-
98-
$logger.info("Executing hook");
97+
module.exports = function ($injector, hookArgs) {
98+
const $logger = $injector.resolve('$logger')
99+
const $fs = $injector.resolve('$fs')
100+
101+
$logger.info('Executing hook')
99102
}
100103
```
101104

102105
**Important Notes:**
106+
103107
- Injected dependencies are resolved by name
104108
- If you inject a non-existent service (e.g., `$logger1`), the CLI won't execute the hook and will show a warning
105109
- When using `$injector` directly, no warning is shown for incorrect service names, and an error will be thrown during execution
@@ -109,30 +113,30 @@ module.exports = function($injector, hookArgs) {
109113
NativeScript CLI supports asynchronous code in hooks. If executing async code, you must return a Promise:
110114

111115
```javascript
112-
var mkdirp = require('mkdirp');
116+
var mkdirp = require('mkdirp')
113117

114-
module.exports = function($logger) {
115-
return new Promise(function(resolve, reject) {
116-
mkdirp('somedir', function(err) {
118+
module.exports = function ($logger) {
119+
return new Promise(function (resolve, reject) {
120+
mkdirp('somedir', function (err) {
117121
if (err) {
118-
reject(err);
122+
reject(err)
119123
} else {
120-
resolve();
124+
resolve()
121125
}
122-
});
123-
});
126+
})
127+
})
124128
}
125129
```
126130

127131
## Spawned Hooks
128132

129133
Spawned hooks are executed via Node's `child_process.spawn` from the project's root directory. All options are passed to the script using environment variables:
130134

131-
| Environment Variable | Description |
132-
|---------------------|-------------|
133-
| `TNS-VERSION` | The version of the NativeScript CLI |
134-
| `TNS-HOOK_FULL_PATH` | The full path to the executed hook |
135-
| `TNS-COMMANDLINE` | The exact command-line arguments passed to NativeScript CLI (e.g., `tns run ios --emulator`) |
135+
| Environment Variable | Description |
136+
| -------------------- | -------------------------------------------------------------------------------------------- |
137+
| `TNS-VERSION` | The version of the NativeScript CLI |
138+
| `TNS-HOOK_FULL_PATH` | The full path to the executed hook |
139+
| `TNS-COMMANDLINE` | The exact command-line arguments passed to NativeScript CLI (e.g., `tns run ios --emulator`) |
136140

137141
If a spawned hook returns a non-zero exit code, NativeScript CLI will throw an error and abort the command's execution.
138142

@@ -143,9 +147,9 @@ If a spawned hook returns a non-zero exit code, NativeScript CLI will throw an e
143147
Hooks can execute code before or after a specific action:
144148

145149
```javascript
146-
module.exports = function(hookArgs) {
150+
module.exports = function (hookArgs) {
147151
if (hookArgs.prepareData.release) {
148-
console.log("Before executing release build.");
152+
console.log('Before executing release build.')
149153
}
150154
}
151155
```
@@ -155,9 +159,9 @@ module.exports = function(hookArgs) {
155159
Hooks can replace the original CLI function (use sparingly):
156160

157161
```javascript
158-
module.exports = function(hookArgs, $logger) {
162+
module.exports = function (hookArgs, $logger) {
159163
return () => {
160-
$logger.info("Replaced the original CLI function.");
164+
$logger.info('Replaced the original CLI function.')
161165
}
162166
}
163167
```
@@ -175,6 +179,7 @@ npm install nativescript-hook --save
175179
```
176180

177181
For NativeScript 7+, use:
182+
178183
```bash
179184
npm install @nativescript/hook --save
180185
```
@@ -184,8 +189,8 @@ npm install @nativescript/hook --save
184189
Create `postinstall.js` at the root folder of your plugin:
185190

186191
```javascript
187-
var hook = require("nativescript-hook")(__dirname);
188-
hook.postinstall();
192+
var hook = require('nativescript-hook')(__dirname)
193+
hook.postinstall()
189194

190195
// For NativeScript 7+:
191196
// require('@nativescript/hook')(__dirname).postinstall();
@@ -196,8 +201,8 @@ hook.postinstall();
196201
Create `preuninstall.js` at the root folder of your plugin:
197202

198203
```javascript
199-
var hook = require("nativescript-hook")(__dirname);
200-
hook.preuninstall();
204+
var hook = require('nativescript-hook')(__dirname)
205+
hook.preuninstall()
201206

202207
// For NativeScript 7+:
203208
// require('@nativescript/hook')(__dirname).preuninstall();
@@ -241,15 +246,19 @@ Define your hooks under the `nativescript` property:
241246
### Hook Configuration Properties
242247

243248
#### type (Required)
249+
244250
Specifies when the hook should execute. Format: `before-<hookName>` or `after-<hookName>`
245251

246252
#### script (Required)
253+
247254
The relative path from the plugin root to the hook implementation file.
248255

249256
#### inject (Optional)
257+
250258
Boolean property indicating whether the hook should be executed in-process (`true`) or spawned (`false`). When `inject: true`, the hook can access NativeScript CLI services.
251259

252260
#### name (Optional)
261+
253262
Custom name for the hook. Defaults to the plugin package name.
254263

255264
**Example with custom name:**
@@ -282,13 +291,13 @@ export default {
282291
hooks: [
283292
{
284293
type: 'before-prepare',
285-
script: './scripts/hooks/before-prepare.js'
294+
script: './scripts/hooks/before-prepare.js',
286295
},
287296
{
288297
type: 'after-prepare',
289-
script: './scripts/hooks/after-prepare.js'
290-
}
291-
]
298+
script: './scripts/hooks/after-prepare.js',
299+
},
300+
],
292301
} as NativeScriptConfig
293302
```
294303

@@ -309,28 +318,28 @@ hooks: [
309318

310319
The following hook types are available (prefix with `before-` or `after-`):
311320

312-
| Hook Name | Description | Execution Context |
313-
|-----------|-------------|-------------------|
314-
| `buildAndroidPlugin` | Builds aar file for Android plugin | Runs during `prepareNativeApp` |
315-
| `buildAndroid` | Builds Android app | During Android build process |
316-
| `buildIOS` | Builds iOS app | During iOS build process |
317-
| `checkEnvironment` | Validates project environment | Runs during `ns doctor`, `ns clean`, and most build commands |
318-
| `checkForChanges` | Detects changes during watch | NativeScript CLI checks application state to decide if rebuild/reinstall/restart is needed |
319-
| `install` | Application installed to device/emulator | After app installation |
320-
| `prepare` | Compiles webpack and prepares native app | Prepares the application in platforms folder |
321-
| `prepareNativeApp` | Prepares the actual native app | Runs during `prepare`/`watch` hook |
322-
| `resolveCommand` | Resolves command and arguments | Runs before all CLI commands |
323-
| `watch` | Sets up watchers for live sync | During `prepare` hook for live development |
324-
| `watchPatterns` | Sets up watch patterns | During `watch` hook |
321+
| Hook Name | Description | Execution Context |
322+
| -------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ |
323+
| `buildAndroidPlugin` | Builds aar file for Android plugin | Runs during `prepareNativeApp` |
324+
| `buildAndroid` | Builds Android app | During Android build process |
325+
| `buildIOS` | Builds iOS app | During iOS build process |
326+
| `checkEnvironment` | Validates project environment | Runs during `ns doctor`, `ns clean`, and most build commands |
327+
| `checkForChanges` | Detects changes during watch | NativeScript CLI checks application state to decide if rebuild/reinstall/restart is needed |
328+
| `install` | Application installed to device/emulator | After app installation |
329+
| `prepare` | Compiles webpack and prepares native app | Prepares the application in platforms folder |
330+
| `prepareNativeApp` | Prepares the actual native app | Runs during `prepare`/`watch` hook |
331+
| `resolveCommand` | Resolves command and arguments | Runs before all CLI commands |
332+
| `watch` | Sets up watchers for live sync | During `prepare` hook for live development |
333+
| `watchPatterns` | Sets up watch patterns | During `watch` hook |
325334

326335
### Hook Execution Examples
327336

328337
**Example: Release Build Check**
329338

330339
```javascript
331-
module.exports = function(hookArgs) {
340+
module.exports = function (hookArgs) {
332341
if (hookArgs.prepareData.release) {
333-
console.log("Executing release build");
342+
console.log('Executing release build')
334343
// Modify API keys, enable production features, etc.
335344
}
336345
}
@@ -339,53 +348,77 @@ module.exports = function(hookArgs) {
339348
**Example: Using NativeScript Services**
340349

341350
```javascript
342-
module.exports = function($logger, $fs, hookArgs) {
343-
$logger.info("Starting custom hook");
344-
345-
const configPath = hookArgs.projectData.projectDir + '/config.json';
351+
module.exports = function ($logger, $fs, hookArgs) {
352+
$logger.info('Starting custom hook')
353+
354+
const configPath = hookArgs.projectData.projectDir + '/config.json'
346355
if ($fs.exists(configPath)) {
347-
const config = $fs.readJson(configPath);
348-
$logger.info(`Loaded config: ${JSON.stringify(config)}`);
356+
const config = $fs.readJson(configPath)
357+
$logger.info(`Loaded config: ${JSON.stringify(config)}`)
349358
}
350359
}
351360
```
352361

353362
**Example: ESM Hook**
354363
In a file named `<name>.mjs`
364+
355365
```javascript
356366
export default function (hookArgs) {
357367
console.log(
358-
"MJS executing release build.",
359-
JSON.stringify(hookArgs.prepareData)
360-
);
368+
'MJS executing release build.',
369+
JSON.stringify(hookArgs.prepareData),
370+
)
361371
}
362-
363372
```
364373

365374
## Hooks CLI Command
366375

367-
Starting with NativeScript 9.0 CLI (`npm install -g nativescript`), a new commmand `ns hooks` is available.
376+
As described above these hooks are installed automatically through npm postinstall scripts included in the plugin package.
368377

369-
As described above `postinstall` scripts are used by plugins to install hooks to the correct location for execution, this is not compatible with users
370-
using `npm install --ignore-scripts` ( or other settings which prevent script execution ) given that plugin that requires hooks utilises post install events to install the hooks.
378+
However, if you (or your CI environment) install dependencies with:
379+
380+
```
381+
npm install --ignore-scripts
382+
```
383+
384+
then those postinstall scripts don’t run, which means:
385+
386+
- Plugin hooks aren’t copied to the correct location (`hooks/` folder).
387+
- Builds may fail or certain plugin functionality won’t work.
388+
389+
Starting with NativeScript 9.0 CLI (`npm install -g nativescript`), a new command was introduced:
390+
391+
```
392+
ns hooks
393+
```
394+
395+
This command installs all plugin hooks after dependencies have been installed.
396+
397+
So if your environment blocks postinstall scripts, you can now safely do:
398+
399+
```
400+
npm install --ignore-scripts
401+
ns hooks
402+
```
371403

372-
The new `ns hooks` command resolves this by providing a mechanism to install plugin hooks after a `npm install`.
404+
The new `ns hooks` command will install the hooks into the proper project locations (as postinstall would have done).
373405

374-
**Commands**
406+
**Available Commands**
375407

376-
| Command | Description |
377-
| ---------- | ------------ |
378-
| `ns hooks` | Lists the hooks that are in the installed plugins ( also `ns hooks list` ). |
379-
| `ns hooks install` | Installs the hooks. |
380-
| `ns hooks lock` | Creates a `nativescript-lock.json` file, this is a list of hooks per plugin and a hash of the script for each hook. |
381-
| `ns hooks verify` | Compares the hooks in the plugins with what is specified in the `nativescript-lock.json` file, failing if a hook is not listed or the hash is not the same. |
408+
| Command | Description |
409+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
410+
| `ns hooks` | Lists the hooks that are in the installed plugins ( also `ns hooks list` ). |
411+
| `ns hooks install` | Installs the hooks. |
412+
| `ns hooks lock` | Creates a `nativescript-lock.json` file, this is a list of hooks per plugin and a hash of the script for each hook. |
413+
| `ns hooks verify` | Compares the hooks in the plugins with what is specified in the `nativescript-lock.json` file, failing if a hook is not listed or the hash is not the same. |
382414

383-
**Example usage**
415+
**For extra peace of mind**
384416

385-
* Modify/Create `.npmrc` in the project root adding `ignore-scripts=true`
386-
* After `npm i` run `ns hooks install`
417+
Typically the contents of the hook scripts do not change in plugins from version to version, to prevent unexpecected changes to hooks you can utilize the `lock` command.
387418

388-
For extra peace of mind:
419+
This will:
389420

390-
Run `ns hooks lock` and `ns hooks install` will fail if any of the hooks have changed.
421+
- Create a `nativescript-lock.json` file containing details of the current plugin hooks.
422+
- Ensure that any future `ns hooks install` or `ns hooks verify` invocations will fail if any new hooks are introduced by dependencies or if these hook scripts differ from what was previously installed.
391423

424+
i.e. run `ns hooks lock` and `ns hooks install` will fail if any of the hooks have changed since the last time `ns hooks lock` has been executed.

0 commit comments

Comments
 (0)