Skip to content

Commit 07af906

Browse files
tlancinaimhoffd
authored andcommitted
feat(cordova-serve): support --consolelogs option (#100)
1 parent a52e9bd commit 07af906

File tree

12 files changed

+209
-5
lines changed

12 files changed

+209
-5
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ node_modules
55
!builders/**/schema.d.ts
66
!schematics/**/schema.d.ts
77
!schematics/*/files/**/*
8+
.vscode

assets/consolelog-config.js

Whitespace-only changes.

assets/consolelogs.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Script injected by @ionic/angular-toolkit to send console logs back
2+
// to a websocket server so they can be printed to the terminal
3+
window.Ionic = window.Ionic || {}; window.Ionic.ConsoleLogServer = {
4+
start: function(config) {
5+
var self = this;
6+
7+
this.socket = new WebSocket('ws://' + window.location.hostname + ':' + String(config.wsPort));
8+
this.msgQueue = [];
9+
10+
this.socket.onopen = function() {
11+
self.socketReady = true;
12+
13+
self.socket.onclose = function() {
14+
self.socketReady = false;
15+
console.warn('Console log server closed');
16+
};
17+
};
18+
19+
this.patchConsole();
20+
},
21+
22+
queueMessageSend: function(msg) {
23+
this.msgQueue.push(msg);
24+
this.drainMessageQueue();
25+
},
26+
27+
drainMessageQueue: function() {
28+
var msg;
29+
while (msg = this.msgQueue.shift()) {
30+
if (this.socketReady) {
31+
try {
32+
this.socket.send(JSON.stringify(msg));
33+
} catch(e) {
34+
if (!(e instanceof TypeError)) {
35+
console.error('ws error: ' + e);
36+
}
37+
}
38+
}
39+
}
40+
},
41+
42+
patchConsole: function() {
43+
var self = this;
44+
45+
function _patchConsole(consoleType) {
46+
console[consoleType] = (function() {
47+
var orgConsole = console[consoleType];
48+
return function() {
49+
orgConsole.apply(console, arguments);
50+
var msg = {
51+
category: 'console',
52+
type: consoleType,
53+
data: []
54+
};
55+
for (var i = 0; i < arguments.length; i++) {
56+
msg.data.push(arguments[i]);
57+
}
58+
if (msg.data.length) {
59+
self.queueMessageSend(msg);
60+
}
61+
};
62+
})();
63+
}
64+
65+
// https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-console/#supported-methods
66+
var consoleFns = ['log', 'error', 'exception', 'warn', 'info', 'debug', 'assert', 'dir', 'dirxml', 'time', 'timeEnd', 'table'];
67+
for (var i in consoleFns) {
68+
_patchConsole(consoleFns[i]);
69+
}
70+
},
71+
};
72+
73+
Ionic.ConsoleLogServer.start(Ionic.ConsoleLogServerConfig || {});
File renamed without changes.

builders/cordova-build/index.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BuildEvent, Builder, BuilderConfiguration, BuilderContext, BuilderDescription } from '@angular-devkit/architect';
22
import { BrowserBuilderSchema } from '@angular-devkit/build-angular/src/browser/schema';
33
import { getSystemPath, join, normalize } from '@angular-devkit/core';
4+
import { writeFileSync } from 'fs';
45
import { Observable, of } from 'rxjs';
56
import { concatMap, tap } from 'rxjs/operators';
67

@@ -54,9 +55,27 @@ export class CordovaBuildBuilder implements Builder<CordovaBuildBuilderSchema> {
5455
// by default. Let's keep it around.
5556
browserOptions.deleteOutputPath = false;
5657

58+
if (options.consolelogs) {
59+
// Write the config to a file, and then include that in the bundle so it loads on window
60+
const configPath = getSystemPath(join(normalize(__dirname), '../../assets', normalize('consolelog-config.js')));
61+
writeFileSync(configPath, `window.Ionic = window.Ionic || {}; Ionic.ConsoleLogServerConfig = { wsPort: ${options.consolelogsPort} }`);
62+
63+
browserOptions.scripts.push({
64+
input: configPath,
65+
bundleName: 'consolelogs',
66+
lazy: false,
67+
});
68+
69+
browserOptions.scripts.push({
70+
input: getSystemPath(join(normalize(__dirname), '../../assets', normalize('consolelogs.js'))),
71+
bundleName: 'consolelogs',
72+
lazy: false,
73+
});
74+
}
75+
5776
if (options.cordovaMock) {
5877
browserOptions.scripts.push({
59-
input: getSystemPath(join(normalize(__dirname), normalize('cordova.js'))),
78+
input: getSystemPath(join(normalize(__dirname), '../../assets', normalize('cordova.js'))),
6079
bundleName: 'cordova',
6180
lazy: false,
6281
});

builders/cordova-build/schema.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export interface CordovaBuildBuilderSchema {
55
sourceMap?: boolean;
66
cordovaAssets?: boolean;
77
cordovaMock?: boolean;
8+
consolelogs?: boolean;
9+
consolelogsPort?: number;
810
}

builders/cordova-build/schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77
"type": "string",
88
"description": "Target to build."
99
},
10+
"consolelogs": {
11+
"type": "boolean",
12+
"description": "Inject script to print console logs to the terminal."
13+
},
14+
"consolelogsPort": {
15+
"type": "number",
16+
"description": "Port for console log server.",
17+
"default": 53703
18+
},
1019
"platform": {
1120
"type": "string",
1221
"description": "Cordova platform to use during build."

builders/cordova-serve/index.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import { NormalizedBrowserBuilderSchema } from '@angular-devkit/build-angular/sr
33
import { DevServerBuilder, DevServerBuilderOptions } from '@angular-devkit/build-angular/src/dev-server';
44
import { Path, virtualFs } from '@angular-devkit/core';
55
import * as fs from 'fs';
6-
import { Observable, of } from 'rxjs';
6+
import { Observable, from, of } from 'rxjs';
77
import { concatMap, tap } from 'rxjs/operators';
88

99
import { CordovaBuildBuilder, CordovaBuildBuilderSchema } from '../cordova-build';
1010

11+
import { createConsoleLogServer } from './log-server';
1112
import { CordovaServeBuilderSchema } from './schema';
1213

1314
export class CordovaServeBuilder implements Builder<CordovaServeBuilderSchema> {
@@ -36,9 +37,9 @@ export class CordovaServeBuilder implements Builder<CordovaServeBuilderSchema> {
3637
}
3738

3839
protected _getCordovaBuildConfig(cordovaServeOptions: CordovaServeBuilderSchema): Observable<BuilderConfiguration<CordovaBuildBuilderSchema>> {
39-
const { platform, cordovaBasePath, cordovaAssets, cordovaMock } = cordovaServeOptions;
40+
const { platform, cordovaBasePath, cordovaAssets, cordovaMock, consolelogs, consolelogsPort } = cordovaServeOptions;
4041
const [ project, target, configuration ] = cordovaServeOptions.cordovaBuildTarget.split(':');
41-
const cordovaBuildTargetSpec = { project, target, configuration, overrides: { platform, cordovaBasePath, cordovaAssets, cordovaMock } };
42+
const cordovaBuildTargetSpec = { project, target, configuration, overrides: { platform, cordovaBasePath, cordovaAssets, cordovaMock, consolelogs, consolelogsPort } };
4243
const cordovaBuildTargetConfig = this.context.architect.getBuilderConfiguration<CordovaBuildBuilderSchema>(cordovaBuildTargetSpec);
4344

4445
return this.context.architect.getBuilderDescription(cordovaBuildTargetConfig).pipe(
@@ -52,6 +53,14 @@ class CordovaDevServerBuilder extends DevServerBuilder {
5253
super(context);
5354
}
5455

56+
run(builderConfig: BuilderConfiguration<DevServerBuilderOptions>): Observable<BuildEvent> {
57+
if (this.cordovaBuildOptions.consolelogs && this.cordovaBuildOptions.consolelogsPort) {
58+
return from(createConsoleLogServer(builderConfig.options.host, this.cordovaBuildOptions.consolelogsPort))
59+
.pipe(_ => super.run(builderConfig));
60+
}
61+
return super.run(builderConfig);
62+
}
63+
5564
buildWebpackConfig(root: Path, projectRoot: Path, host: virtualFs.Host<fs.Stats>, browserOptions: NormalizedBrowserBuilderSchema) {
5665
const builder = new CordovaBuildBuilder(this.context);
5766
builder.validateBuilderConfig(this.cordovaBuildOptions);

builders/cordova-serve/log-server.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { terminal } from '@angular-devkit/core';
2+
import * as util from 'util';
3+
import * as WebSocket from 'ws';
4+
5+
export interface ConsoleLogServerMessage {
6+
category: 'console';
7+
type: string;
8+
data: any[];
9+
}
10+
11+
export interface ConsoleLogServerOptions {
12+
consolelogs: boolean;
13+
consolelogsPort: number;
14+
}
15+
16+
export function isConsoleLogServerMessage(m: any): m is ConsoleLogServerMessage {
17+
return m
18+
&& typeof m.category === 'string'
19+
&& typeof m.type === 'string'
20+
&& m.data && typeof m.data.length === 'number';
21+
}
22+
23+
export async function createConsoleLogServer(host: string, port: number): Promise<WebSocket.Server> {
24+
const wss = new WebSocket.Server({ host, port });
25+
26+
wss.on('connection', ws => {
27+
ws.on('message', data => {
28+
let msg;
29+
30+
try {
31+
data = data.toString();
32+
msg = JSON.parse(data);
33+
} catch (e) {
34+
process.stderr.write(`Error parsing JSON message from client: "${data}" ${terminal.red(e.stack ? e.stack : e)}\n`);
35+
return;
36+
}
37+
38+
if (!isConsoleLogServerMessage(msg)) {
39+
const m = util.inspect(msg, { colors: true });
40+
process.stderr.write(`Bad format in client message: ${m}\n`);
41+
return;
42+
}
43+
44+
if (msg.category === 'console') {
45+
let status: ((_: string) => string) | undefined;
46+
47+
if (msg.type === 'info' || msg.type === 'log') {
48+
status = terminal.reset;
49+
} else if (msg.type === 'error') {
50+
status = terminal.red;
51+
} else if (msg.type === 'warn') {
52+
status = terminal.yellow;
53+
}
54+
55+
// pretty print objects and arrays (no newlines for arrays)
56+
msg.data = msg.data.map(d => JSON.stringify(d, undefined, d && d.length ? '' : ' '));
57+
58+
if (status) {
59+
process.stdout.write(`[${status('console.' + msg.type)}]: ${msg.data.join(' ')}\n`);
60+
} else {
61+
process.stdout.write(`[console]: ${msg.data.join(' ')}\n`);
62+
}
63+
}
64+
});
65+
66+
ws.on('error', (err: NodeJS.ErrnoException) => {
67+
if (err && err.code !== 'ECONNRESET') {
68+
process.stderr.write(`There was an error with the logging stream: ${JSON.stringify(err)}\n`);
69+
}
70+
});
71+
});
72+
73+
wss.on('error', (err: NodeJS.ErrnoException) => {
74+
process.stderr.write(`There was an error with the logging websocket: ${JSON.stringify(err)}\n`);
75+
});
76+
77+
return wss;
78+
}

builders/cordova-serve/schema.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ export interface CordovaServeBuilderSchema {
99
sourceMap?: boolean;
1010
cordovaAssets?: boolean;
1111
cordovaMock?: boolean;
12+
consolelogs?: boolean;
13+
consolelogsPort?: number;
1214
}

builders/cordova-serve/schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77
"type": "string",
88
"description": "Target to use for build."
99
},
10+
"consolelogs": {
11+
"type": "boolean",
12+
"description": "Print console logs to the terminal."
13+
},
14+
"consolelogsPort": {
15+
"type": "number",
16+
"description": "Port for console log server.",
17+
"default": 53703
18+
},
1019
"devServerTarget": {
1120
"type": "string",
1221
"description": "Target to use for serve."

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"dependencies": {
3333
"@schematics/angular": "^7.0.3",
3434
"tslib": "^1.9.0",
35-
"typescript": "^3.2.4"
35+
"typescript": "^3.2.4",
36+
"ws": "^6.1.4"
3637
},
3738
"devDependencies": {
3839
"@angular-devkit/architect": "0.13.1",
@@ -46,6 +47,7 @@
4647
"@types/node": "^8.10.34",
4748
"@types/webpack": "^4.4.14",
4849
"@types/webpack-dev-server": "^3.1.1",
50+
"@types/ws": "^6.0.1",
4951
"commitizen": "^3.0.2",
5052
"cz-conventional-changelog": "^2.1.0",
5153
"husky": "^1.1.1",

0 commit comments

Comments
 (0)