Skip to content
This repository was archived by the owner on Apr 4, 2025. It is now read-only.

Commit 2e14a37

Browse files
committed
feat(@angular-devkit/core): add support for color and no-color
And namespace the terminal.
1 parent bcf8d19 commit 2e14a37

5 files changed

Lines changed: 235 additions & 4 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import ReadableStream = NodeJS.ReadableStream;
9+
import WriteStream = NodeJS.WriteStream;
10+
import Socket = NodeJS.Socket;
11+
12+
13+
/**
14+
* Node specific stuff.
15+
*/
16+
declare const process: {
17+
env: { [name: string]: string };
18+
platform: string;
19+
versions: {
20+
node: string;
21+
};
22+
23+
stdin: ReadableStream;
24+
stdout: WriteStream;
25+
stderr: WriteStream;
26+
};
27+
declare const os: {
28+
release: () => string;
29+
};
30+
31+
32+
const _env = (typeof process == 'object' && process.env) || {};
33+
const _platform = (typeof process == 'object' && process.platform) || '';
34+
const _versions = (typeof process == 'object' && process.versions) || { node: '' };
35+
const _os = (typeof os == 'object' && os) || { release: () => '' };
36+
37+
const streamMap = new WeakMap<{}, StreamCapabilities>();
38+
39+
40+
export interface StreamCapabilities {
41+
readable: boolean;
42+
writable: boolean;
43+
44+
/**
45+
* Supports text. This should be true for any streams.
46+
*/
47+
text: boolean;
48+
49+
/**
50+
* Supports colors (16 colors).
51+
*/
52+
colors: boolean;
53+
54+
/**
55+
* Supports 256 colors.
56+
*/
57+
color256: boolean;
58+
59+
/**
60+
* Supports 16 millions (3x8-bit channels) colors.
61+
*/
62+
color16m: boolean;
63+
64+
/**
65+
* Height of the terminal. If the stream is not tied to a terminal, will be null.
66+
*/
67+
rows: number | null;
68+
69+
/**
70+
* Width of the terminal. If the stream is not tied to a terminal, will be null.
71+
*/
72+
columns: number | null;
73+
}
74+
75+
76+
const ciVars = ['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'];
77+
78+
79+
function _getColorLevel(stream: Socket): number {
80+
if (stream && !stream.isTTY) {
81+
return 0;
82+
}
83+
84+
if (_platform.startsWith('win32')) {
85+
// Node.js 7.5.0 is the first version of Node.js to include a patch to
86+
// libuv that enables 256 color output on Windows. Anything earlier and it
87+
// won't work. However, here we target Node.js 8 at minimum as it is an LTS
88+
// release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows
89+
// release that supports 256 colors.
90+
const osRelease = _os.release().split('.');
91+
if (Number(_versions.node.split('.')[0]) >= 8
92+
&& Number(osRelease[0]) >= 10
93+
&& Number(osRelease[2]) >= 10586) {
94+
return 2;
95+
}
96+
97+
return 1;
98+
}
99+
100+
if ('CI' in _env) {
101+
if (ciVars.some(sign => sign in _env) || _env.CI_NAME === 'codeship') {
102+
return 1;
103+
}
104+
105+
return 0;
106+
}
107+
108+
if ('TEAMCITY_VERSION' in _env) {
109+
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(_env.TEAMCITY_VERSION) ? 1 : 0;
110+
}
111+
112+
if ('TERM_PROGRAM' in _env) {
113+
const version = parseInt((_env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
114+
115+
switch (_env.TERM_PROGRAM) {
116+
case 'iTerm.app':
117+
return version >= 3 ? 3 : 2;
118+
case 'Hyper':
119+
return 3;
120+
case 'Apple_Terminal':
121+
return 2;
122+
123+
// No default
124+
}
125+
}
126+
127+
if (/-256(color)?$/i.test(_env.TERM)) {
128+
return 2;
129+
}
130+
131+
if (/^screen|^xterm|^vt100|^rxvt|color|ansi|cygwin|linux/i.test(_env.TERM)) {
132+
return 1;
133+
}
134+
135+
if ('COLORTERM' in _env) {
136+
return 1;
137+
}
138+
139+
if (_env.TERM === 'dumb') {
140+
return 0;
141+
}
142+
143+
return 0;
144+
}
145+
146+
147+
function _getRows() {
148+
return process.stdout.rows || null;
149+
}
150+
function _getColumns() {
151+
return process.stdout.columns || null;
152+
}
153+
154+
155+
function _createCapabilities(stream: Socket, isTerminalStream: boolean): StreamCapabilities {
156+
const level = _getColorLevel(stream);
157+
158+
return {
159+
readable: stream.readable,
160+
writable: stream.writable,
161+
text: true,
162+
163+
colors: level > 0,
164+
color256: level > 1,
165+
color16m: level > 2,
166+
167+
rows: isTerminalStream ? _getRows() : null,
168+
columns: isTerminalStream ? _getColumns() : null,
169+
};
170+
}
171+
172+
173+
export function getCapabilities(
174+
stream: Socket,
175+
isTerminalStream = !!stream.isTTY,
176+
): StreamCapabilities {
177+
let maybeCaps = streamMap.get(stream);
178+
if (!maybeCaps) {
179+
maybeCaps = _createCapabilities(stream, isTerminalStream);
180+
streamMap.set(stream, maybeCaps);
181+
}
182+
183+
return maybeCaps;
184+
}
185+
186+
187+
export const stdin = getCapabilities(process.stdin as Socket);
188+
export const stdout = getCapabilities(process.stdout);
189+
export const stderr = getCapabilities(process.stderr);
File renamed without changes.

packages/angular_devkit/core/src/terminal/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,7 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
export * from './ansi';
8+
export * from './text';
9+
10+
import * as colors from './colors';
11+
export { colors };
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import * as caps from './caps';
9+
import * as ansi from './colors';
10+
11+
12+
export const reset = caps.stdout.colors ? ansi.reset : (x: string) => x;
13+
export const bold = caps.stdout.colors ? ansi.bold : (x: string) => x;
14+
export const dim = caps.stdout.colors ? ansi.dim : (x: string) => x;
15+
export const italic = caps.stdout.colors ? ansi.italic : (x: string) => x;
16+
export const underline = caps.stdout.colors ? ansi.underline : (x: string) => x;
17+
export const inverse = caps.stdout.colors ? ansi.inverse : (x: string) => x;
18+
export const hidden = caps.stdout.colors ? ansi.hidden : (x: string) => x;
19+
export const strikethrough = caps.stdout.colors ? ansi.strikethrough : (x: string) => x;
20+
21+
export const black = caps.stdout.colors ? ansi.black : (x: string) => x;
22+
export const red = caps.stdout.colors ? ansi.red : (x: string) => x;
23+
export const green = caps.stdout.colors ? ansi.green : (x: string) => x;
24+
export const yellow = caps.stdout.colors ? ansi.yellow : (x: string) => x;
25+
export const blue = caps.stdout.colors ? ansi.blue : (x: string) => x;
26+
export const magenta = caps.stdout.colors ? ansi.magenta : (x: string) => x;
27+
export const cyan = caps.stdout.colors ? ansi.cyan : (x: string) => x;
28+
export const white = caps.stdout.colors ? ansi.white : (x: string) => x;
29+
export const grey = caps.stdout.colors ? ansi.gray : (x: string) => x;
30+
export const gray = caps.stdout.colors ? ansi.gray : (x: string) => x;
31+
32+
export const bgBlack = caps.stdout.colors ? ansi.bgBlack : (x: string) => x;
33+
export const bgRed = caps.stdout.colors ? ansi.bgRed : (x: string) => x;
34+
export const bgGreen = caps.stdout.colors ? ansi.bgGreen : (x: string) => x;
35+
export const bgYellow = caps.stdout.colors ? ansi.bgYellow : (x: string) => x;
36+
export const bgBlue = caps.stdout.colors ? ansi.bgBlue : (x: string) => x;
37+
export const bgMagenta = caps.stdout.colors ? ansi.bgMagenta : (x: string) => x;
38+
export const bgCyan = caps.stdout.colors ? ansi.bgCyan : (x: string) => x;
39+
export const bgWhite = caps.stdout.colors ? ansi.bgWhite : (x: string) => x;

scripts/benchmark.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { tags, terminal } from '@angular-devkit/core';
99
import * as glob from 'glob';
1010
import 'jasmine';
11-
import {SpecReporter as JasmineSpecReporter } from 'jasmine-spec-reporter';
11+
import { SpecReporter as JasmineSpecReporter } from 'jasmine-spec-reporter';
1212
import { join, relative } from 'path';
1313

1414

@@ -84,7 +84,7 @@ class BenchmarkReporter extends JasmineSpecReporter implements jasmine.CustomRep
8484
const baseAverage = pad(Math.floor(stat.base.average));
8585
const baseAverageMult = pad(precision(stat.average / stat.base.average), multPad);
8686

87-
console.log(terminal.yellow(tags.indentBy(6)`
87+
console.log(terminal.colors.yellow(tags.indentBy(6)`
8888
fastest: ${fastest}
8989
(base) ${baseFastest}
9090
slowest: ${slowest}
@@ -93,7 +93,7 @@ class BenchmarkReporter extends JasmineSpecReporter implements jasmine.CustomRep
9393
average: ${average} (${baseAverage}) (${baseAverageMult}x)
9494
`));
9595
} else {
96-
console.log(terminal.yellow(tags.indentBy(6)`
96+
console.log(terminal.colors.yellow(tags.indentBy(6)`
9797
fastest: ${fastest}
9898
slowest: ${slowest}
9999
mean: ${mean}

0 commit comments

Comments
 (0)