Skip to content

Commit c110f32

Browse files
committed
feat(hooks): new hooks package w/ cordova engine hooks
1 parent 943ba67 commit c110f32

25 files changed

Lines changed: 371 additions & 94 deletions

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.js
2+
utils
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
src
2+
tsconfig.json

packages/@ionic/cli-hooks/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Change Log

packages/@ionic/cli-hooks/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2018 Drifty Co
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+
of the Software, and to permit persons to whom the Software is furnished to do
10+
so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# CLI Hooks
2+
3+
A collection of useful hook scripts for Ionic apps.
4+
5+
## Install
6+
7+
```bash
8+
$ npm i @ionic/cli-hooks
9+
```
10+
11+
## Use
12+
13+
Modify your `ionic.config.json` file to add a `hooks` object:
14+
15+
```json
16+
"hooks": {
17+
"<hook>": [
18+
"node_modules/@ionic/cli-hooks/<file>.js"
19+
]
20+
}
21+
```
22+
23+
Replace `<hook>` with the name of the hook you want to use and `<file>` with
24+
the file name within this package.
25+
26+
See [CLI Hook
27+
documentation](https://ionicframework.com/docs/cli/configuring.html#hooks) for
28+
details.
29+
30+
## List
31+
32+
* `node_modules/@ionic/cli-hooks/prepare-cordova-engine.js`: Before a build, insert `cordova.js` script tag into your `index.html` file if it does not already exist.
33+
* `node_modules/@ionic/cli-hooks/revert-cordova-engine.js`: After a build, restore the original `index.html` file.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "@ionic/cli-hooks",
3+
"version": "0.0.0",
4+
"description": "Useful hook scripts for Ionic apps",
5+
"homepage": "https://ionicframework.com/",
6+
"author": "Ionic Team <[email protected]> (https://ionic.io)",
7+
"license": "MIT",
8+
"scripts": {
9+
"clean": "rimraf *.js utils",
10+
"lint": "tslint --config ../../../tslint.js --project tsconfig.json",
11+
"build": "npm run clean && tsc",
12+
"watch": "tsc -w",
13+
"prepublishOnly": "npm run build"
14+
},
15+
"dependencies": {
16+
"parse5": "^4.0.0",
17+
"tslib": "^1.9.0"
18+
},
19+
"devDependencies": {
20+
"@ionic/cli-utils": "*",
21+
"rimraf": "^2.6.2"
22+
},
23+
"repository": {
24+
"type": "git",
25+
"url": "https://github.com/ionic-team/ionic-cli.git"
26+
},
27+
"bugs": {
28+
"url": "https://github.com/ionic-team/ionic-cli/issues"
29+
},
30+
"jest": {
31+
"globals": {
32+
"ts-jest": {
33+
"tsConfigFile": "tsconfig.json"
34+
}
35+
},
36+
"moduleFileExtensions": [
37+
"ts",
38+
"js"
39+
],
40+
"transform": {
41+
".(ts)": "<rootDir>/../../../node_modules/ts-jest/preprocessor.js"
42+
},
43+
"testRegex": "/__tests__/.*\\.(ts|js)$"
44+
}
45+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import * as path from 'path';
2+
3+
import * as parse5 from 'parse5';
4+
import { HookContext } from '@ionic/cli-utils';
5+
6+
import { readFile, writeFile } from './utils/fs';
7+
import { findElementByAttribute, findElementByTag, findElementsByTag } from './utils/html';
8+
9+
const ta = parse5.treeAdapters.default;
10+
11+
export default async function (ctx: HookContext) {
12+
if (ctx.name !== 'build:before' || ctx.build.engine !== 'cordova') {
13+
return;
14+
}
15+
16+
const indexPath = path.resolve(ctx.project.srcDir, 'index.html');
17+
const origIndexPath = path.resolve(ctx.project.srcDir, 'index.html.orig');
18+
const indexContents = await readFile(indexPath);
19+
20+
const index = parse5.parse(indexContents) as parse5.AST.Default.Document;
21+
const rootNode = findElementByTag(index.childNodes as parse5.AST.Default.Element[], 'html');
22+
23+
if (!rootNode) {
24+
throw new Error('No root node in index.html');
25+
}
26+
27+
const namespaceURI = rootNode.namespaceURI;
28+
29+
const headNode = findElementByTag(rootNode.childNodes as parse5.AST.Default.Element[], 'head');
30+
const bodyNode = findElementByTag(rootNode.childNodes as parse5.AST.Default.Element[], 'body');
31+
32+
if (!headNode || !bodyNode) {
33+
throw new Error('No head or body node in index.html');
34+
}
35+
36+
const cdvattr = { name: 'src', value: 'cordova.js' };
37+
const scriptNodes = findElementsByTag([...headNode.childNodes, ...bodyNode.childNodes] as parse5.AST.Default.Element[], 'script');
38+
const scriptNode = findElementByAttribute(scriptNodes, cdvattr);
39+
40+
if (!scriptNode) {
41+
const el = ta.createElement('script', namespaceURI, [cdvattr]);
42+
ta.appendChild(headNode, el);
43+
}
44+
45+
const serialized = parse5.serialize(index);
46+
47+
await Promise.all([
48+
writeFile(origIndexPath, indexContents),
49+
writeFile(indexPath, serialized),
50+
]);
51+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import * as path from 'path';
2+
3+
import { HookContext } from '@ionic/cli-utils';
4+
5+
import { readFile, unlink, writeFile } from './utils/fs';
6+
7+
export default async function (ctx: HookContext) {
8+
if (ctx.name !== 'build:after' || ctx.build.engine !== 'cordova') {
9+
return;
10+
}
11+
12+
const indexPath = path.resolve(ctx.project.srcDir, 'index.html');
13+
const origIndexPath = path.resolve(ctx.project.srcDir, 'index.html.orig');
14+
const origIndexContents = await readFile(origIndexPath);
15+
16+
await writeFile(indexPath, origIndexContents);
17+
await unlink(origIndexPath);
18+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import * as fs from 'fs';
2+
3+
export async function readFile(p: string): Promise<string> {
4+
return new Promise<string>((resolve, reject) => {
5+
fs.readFile(p, { encoding: 'utf8' }, (err, contents) => {
6+
if (err) {
7+
return reject(err);
8+
}
9+
10+
resolve(contents);
11+
});
12+
});
13+
}
14+
15+
export async function writeFile(p: string, contents: string): Promise<void> {
16+
return new Promise<void>((resolve, reject) => {
17+
fs.writeFile(p, contents, { encoding: 'utf8' }, err => {
18+
if (err) {
19+
return reject(err);
20+
}
21+
22+
resolve();
23+
});
24+
});
25+
}
26+
27+
export async function unlink(p: string): Promise<void> {
28+
return new Promise<void>((resolve, reject) => {
29+
fs.unlink(p, err => {
30+
if (err) {
31+
return reject(err);
32+
}
33+
34+
resolve();
35+
});
36+
});
37+
}

0 commit comments

Comments
 (0)