@parcel/logger 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-present Devon Govett
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do 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.
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ // Node 8 supports native async functions - no need to use compiled code!
2
+ module.exports =
3
+ parseInt(process.versions.node, 10) < 8
4
+ ? require('./lib/Logger')
5
+ : require('./src/Logger');
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@parcel/logger",
3
+ "version": "1.11.0",
4
+ "description": "Blazing fast, zero configuration web application bundler",
5
+ "main": "index.js",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/parcel-bundler/parcel.git"
10
+ },
11
+ "engines": {
12
+ "node": ">= 6.0.0"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "scripts": {
18
+ "test": "cross-env NODE_ENV=test mocha",
19
+ "test-ci": "yarn build && yarn test",
20
+ "format": "prettier --write \"./{src,bin,test}/**/*.{js,json,md}\"",
21
+ "lint": "eslint . && prettier \"./{src,bin,test}/**/*.{js,json,md}\" --list-different",
22
+ "build": "babel src -d lib",
23
+ "prepublish": "yarn build"
24
+ },
25
+ "dependencies": {
26
+ "@parcel/workers": "^1.11.0",
27
+ "chalk": "^2.1.0",
28
+ "grapheme-breaker": "^0.3.2",
29
+ "ora": "^2.1.0",
30
+ "strip-ansi": "^4.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@parcel/babel-register": "^1.11.0",
34
+ "mocha": "^5.2.0",
35
+ "sinon": "^5.0.1"
36
+ },
37
+ "gitHead": "34eb91e8e6991073e594bff731c333d09b0403b5"
38
+ }
package/src/.babelrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "presets": [["@babel/preset-env", {
3
+ "targets": {
4
+ "node": "6"
5
+ }
6
+ }]],
7
+ "plugins": ["@babel/plugin-transform-runtime"]
8
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "../../../../.eslintrc.json"
3
+ }
package/src/Logger.js ADDED
@@ -0,0 +1,248 @@
1
+ const chalk = require('chalk');
2
+ const readline = require('readline');
3
+ const prettyError = require('./prettyError');
4
+ const emoji = require('./emoji');
5
+ const {countBreaks} = require('grapheme-breaker');
6
+ const stripAnsi = require('strip-ansi');
7
+ const ora = require('ora');
8
+ const WorkerFarm = require('@parcel/workers');
9
+ const path = require('path');
10
+ const fs = require('fs');
11
+
12
+ class Logger {
13
+ constructor(options) {
14
+ this.lines = 0;
15
+ this.spinner = null;
16
+ this.setOptions(options);
17
+ }
18
+
19
+ setOptions(options) {
20
+ this.logLevel =
21
+ options && isNaN(options.logLevel) === false
22
+ ? Number(options.logLevel)
23
+ : 3;
24
+ this.color =
25
+ options && typeof options.color === 'boolean'
26
+ ? options.color
27
+ : chalk.supportsColor;
28
+ this.emoji = (options && options.emoji) || emoji;
29
+ this.chalk = new chalk.constructor({enabled: this.color});
30
+ this.isTest =
31
+ options && typeof options.isTest === 'boolean'
32
+ ? options.isTest
33
+ : process.env.NODE_ENV === 'test';
34
+ }
35
+
36
+ countLines(message) {
37
+ return stripAnsi(message)
38
+ .split('\n')
39
+ .reduce((p, line) => {
40
+ if (process.stdout.columns) {
41
+ return p + Math.ceil((line.length || 1) / process.stdout.columns);
42
+ }
43
+
44
+ return p + 1;
45
+ }, 0);
46
+ }
47
+
48
+ writeRaw(message) {
49
+ this.stopSpinner();
50
+
51
+ this.lines += this.countLines(message) - 1;
52
+ process.stdout.write(message);
53
+ }
54
+
55
+ write(message, persistent = false) {
56
+ if (this.logLevel > 3) {
57
+ return this.verbose(message);
58
+ }
59
+
60
+ if (!persistent) {
61
+ this.lines += this.countLines(message);
62
+ }
63
+
64
+ this.stopSpinner();
65
+ this._log(message);
66
+ }
67
+
68
+ verbose(message) {
69
+ if (this.logLevel < 4) {
70
+ return;
71
+ }
72
+
73
+ let currDate = new Date();
74
+ message = `[${currDate.toLocaleTimeString()}]: ${message}`;
75
+ if (this.logLevel > 4) {
76
+ if (!this.logFile) {
77
+ this.logFile = fs.createWriteStream(
78
+ path.join(process.cwd(), `parcel-debug-${currDate.toISOString()}.log`)
79
+ );
80
+ }
81
+ this.logFile.write(stripAnsi(message) + '\n');
82
+ }
83
+ this._log(message);
84
+ }
85
+
86
+ log(message) {
87
+ if (this.logLevel < 3) {
88
+ return;
89
+ }
90
+
91
+ this.write(message);
92
+ }
93
+
94
+ persistent(message) {
95
+ if (this.logLevel < 3) {
96
+ return;
97
+ }
98
+
99
+ this.write(this.chalk.bold(message), true);
100
+ }
101
+
102
+ warn(err) {
103
+ if (this.logLevel < 2) {
104
+ return;
105
+ }
106
+
107
+ this._writeError(err, this.emoji.warning, this.chalk.yellow);
108
+ }
109
+
110
+ error(err) {
111
+ if (this.logLevel < 1) {
112
+ return;
113
+ }
114
+
115
+ this._writeError(err, this.emoji.error, this.chalk.red.bold);
116
+ }
117
+
118
+ success(message) {
119
+ this.log(`${this.emoji.success} ${this.chalk.green.bold(message)}`);
120
+ }
121
+
122
+ formatError(err, opts) {
123
+ return prettyError(err, opts);
124
+ }
125
+
126
+ _writeError(err, emoji, color) {
127
+ let {message, stack} = this.formatError(err, {color: this.color});
128
+ this.write(color(`${emoji} ${message}`));
129
+ if (stack) {
130
+ this.write(stack);
131
+ }
132
+ }
133
+
134
+ clear() {
135
+ if (!this.color || this.isTest || this.logLevel > 3) {
136
+ return;
137
+ }
138
+
139
+ while (this.lines > 0) {
140
+ readline.clearLine(process.stdout, 0);
141
+ readline.moveCursor(process.stdout, 0, -1);
142
+ this.lines--;
143
+ }
144
+
145
+ readline.cursorTo(process.stdout, 0);
146
+ this.stopSpinner();
147
+ }
148
+
149
+ progress(message) {
150
+ if (this.logLevel < 3) {
151
+ return;
152
+ }
153
+
154
+ if (this.logLevel > 3) {
155
+ return this.verbose(message);
156
+ }
157
+
158
+ let styledMessage = this.chalk.gray.bold(message);
159
+ if (!this.spinner) {
160
+ this.spinner = ora({
161
+ text: styledMessage,
162
+ stream: process.stdout,
163
+ enabled: this.isTest ? false : undefined // fall back to ora default unless we need to explicitly disable it.
164
+ }).start();
165
+ } else {
166
+ this.spinner.text = styledMessage;
167
+ }
168
+ }
169
+
170
+ stopSpinner() {
171
+ if (this.spinner) {
172
+ this.spinner.stop();
173
+ this.spinner = null;
174
+ }
175
+ }
176
+
177
+ handleMessage(options) {
178
+ this[options.method](...options.args);
179
+ }
180
+
181
+ _log(message) {
182
+ // eslint-disable-next-line no-console
183
+ console.log(message);
184
+ }
185
+
186
+ table(columns, table) {
187
+ // Measure column widths
188
+ let colWidths = [];
189
+ for (let row of table) {
190
+ let i = 0;
191
+ for (let item of row) {
192
+ colWidths[i] = Math.max(colWidths[i] || 0, stringWidth(item));
193
+ i++;
194
+ }
195
+ }
196
+
197
+ // Render rows
198
+ for (let row of table) {
199
+ let items = row.map((item, i) => {
200
+ // Add padding between columns unless the alignment is the opposite to the
201
+ // next column and pad to the column width.
202
+ let padding =
203
+ !columns[i + 1] || columns[i + 1].align === columns[i].align ? 4 : 0;
204
+ return pad(item, colWidths[i] + padding, columns[i].align);
205
+ });
206
+
207
+ this.log(items.join(''));
208
+ }
209
+ }
210
+ }
211
+
212
+ // Pad a string with spaces on either side
213
+ function pad(text, length, align = 'left') {
214
+ let pad = ' '.repeat(length - stringWidth(text));
215
+ if (align === 'right') {
216
+ return pad + text;
217
+ }
218
+
219
+ return text + pad;
220
+ }
221
+
222
+ // Count visible characters in a string
223
+ function stringWidth(string) {
224
+ return countBreaks(stripAnsi('' + string));
225
+ }
226
+
227
+ // If we are in a worker, make a proxy class which will
228
+ // send the logger calls to the main process via IPC.
229
+ // These are handled in WorkerFarm and directed to handleMessage above.
230
+ if (WorkerFarm.isWorker()) {
231
+ class LoggerProxy {}
232
+ for (let method of Object.getOwnPropertyNames(Logger.prototype)) {
233
+ LoggerProxy.prototype[method] = (...args) => {
234
+ WorkerFarm.callMaster(
235
+ {
236
+ location: __filename,
237
+ method,
238
+ args
239
+ },
240
+ false
241
+ );
242
+ };
243
+ }
244
+
245
+ module.exports = new LoggerProxy();
246
+ } else {
247
+ module.exports = new Logger();
248
+ }
package/src/emoji.js ADDED
@@ -0,0 +1,8 @@
1
+ const supportsEmoji =
2
+ process.platform !== 'win32' || process.env.TERM === 'xterm-256color';
3
+
4
+ // Fallback symbols for Windows from https://en.wikipedia.org/wiki/Code_page_437
5
+ exports.progress = supportsEmoji ? '⏳' : '∞';
6
+ exports.success = supportsEmoji ? '✨' : '√';
7
+ exports.error = supportsEmoji ? '🚨' : '×';
8
+ exports.warning = supportsEmoji ? '⚠️' : '‼';
@@ -0,0 +1,24 @@
1
+ module.exports = function(err, opts = {}) {
2
+ let message = typeof err === 'string' ? err : err.message;
3
+ if (!message) {
4
+ message = 'Unknown error';
5
+ }
6
+
7
+ if (err.fileName) {
8
+ let fileName = err.fileName;
9
+ if (err.loc) {
10
+ fileName += `:${err.loc.line}:${err.loc.column}`;
11
+ }
12
+
13
+ message = `${fileName}: ${message}`;
14
+ }
15
+
16
+ let stack;
17
+ if (err.codeFrame) {
18
+ stack = (opts.color && err.highlightedCodeFrame) || err.codeFrame;
19
+ } else if (err.stack) {
20
+ stack = err.stack.slice(err.stack.indexOf('\n') + 1);
21
+ }
22
+
23
+ return {message, stack};
24
+ };
package/test/.babelrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "presets": [["@babel/preset-env", {
3
+ "targets": {
4
+ "node": "current"
5
+ }
6
+ }]],
7
+ "plugins": ["@babel/plugin-transform-runtime"]
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "../../../../.eslintrc.json",
3
+ "env": {
4
+ "mocha": true
5
+ }
6
+ }
package/test/logger.js ADDED
@@ -0,0 +1,123 @@
1
+ const assert = require('assert');
2
+ const sinon = require('sinon');
3
+ const Logger = require('../src/Logger');
4
+
5
+ describe('Logger', () => {
6
+ let log;
7
+ beforeEach(function() {
8
+ log = [];
9
+ });
10
+
11
+ const stub = instance => {
12
+ sinon.stub(instance, '_log').callsFake(message => {
13
+ log.push(message);
14
+ });
15
+ };
16
+
17
+ it('should log message on write', () => {
18
+ const l = new Logger.constructor({});
19
+ stub(l);
20
+
21
+ l.write('hello');
22
+ assert.equal(log[0], 'hello');
23
+ });
24
+
25
+ it('should track number of lines on persist false', () => {
26
+ const l = new Logger.constructor({});
27
+ stub(l);
28
+
29
+ const count = l.lines;
30
+ l.write('hello\nworld', false);
31
+ assert.equal(l.lines, count + 2);
32
+ });
33
+
34
+ it('should not track number of lines on persist true', () => {
35
+ const l = new Logger.constructor({});
36
+ stub(l);
37
+
38
+ const count = l.lines;
39
+ l.write('hello\nworld', true);
40
+ assert.equal(l.lines, count);
41
+ });
42
+
43
+ it('should respect log levels', () => {
44
+ const l = new Logger.constructor({logLevel: 2, color: false});
45
+ stub(l);
46
+
47
+ l.log('message');
48
+ l.persistent('message');
49
+ l.progress('message');
50
+ l.logLevel = 1;
51
+ l.warn('message');
52
+ l.logLevel = 0;
53
+ l.error({message: 'message', stack: 'stack'});
54
+
55
+ assert.equal(log.length, 0);
56
+
57
+ l.logLevel = 1;
58
+ l.error({message: 'message', stack: 'stack'});
59
+ assert.equal(log.length, 2);
60
+
61
+ l.logLevel = 2;
62
+ l.warn('message');
63
+ assert.equal(log.length, 3);
64
+
65
+ l.logLevel = 3;
66
+ l.log('message');
67
+ l.persistent('message');
68
+ l.progress('message');
69
+ assert.equal(log.length, 5);
70
+ });
71
+
72
+ it('should handle lack of color support with alternatives', () => {
73
+ const l = new Logger.constructor({color: false});
74
+ stub(l);
75
+
76
+ // clear is a no-op
77
+ l.lines = 4;
78
+ l.clear();
79
+ assert.equal(l.lines, 4);
80
+ });
81
+
82
+ it('should reset on clear', () => {
83
+ const l = new Logger.constructor({color: true, isTest: false});
84
+ stub(l);
85
+
86
+ // stub readline so we don't actually clear the test output
87
+ const sandbox = sinon.createSandbox();
88
+ sandbox.stub(require('readline'));
89
+
90
+ l.lines = 10;
91
+ l.clear();
92
+
93
+ assert.equal(l.lines, 0);
94
+ sandbox.restore();
95
+ });
96
+
97
+ it('should use ora for progress', () => {
98
+ const l = new Logger.constructor({color: false});
99
+
100
+ l.progress('message');
101
+
102
+ assert(l.spinner);
103
+ assert(l.spinner.text.includes('message'));
104
+ });
105
+
106
+ it('should use internal _log function for writes', () => {
107
+ const l = new Logger.constructor({color: false});
108
+ const sandbox = sinon.createSandbox(); // use sandbox to silence console.log
109
+
110
+ let spy;
111
+ try {
112
+ spy = sandbox.spy(l, '_log');
113
+ sandbox.stub(console, 'log');
114
+
115
+ l.write('hello world');
116
+ } finally {
117
+ l._log.restore();
118
+ sandbox.restore();
119
+ }
120
+
121
+ assert(spy.called);
122
+ });
123
+ });
@@ -0,0 +1,2 @@
1
+ --require @parcel/babel-register
2
+ --exit
@@ -0,0 +1,104 @@
1
+ const assert = require('assert');
2
+ const prettyError = require('../src/prettyError');
3
+
4
+ const message = 'Error Message!';
5
+ const fileName = 'Test.js';
6
+ const codeFrame = '<code>frame</code>';
7
+ const stack =
8
+ 'Error: Uh-oh. Something went wrong. Line 88. \n Oh no. Something else went wrong. Line 77 \n';
9
+
10
+ describe('prettyError', () => {
11
+ it('should handle passing error as string', () => {
12
+ const err = prettyError(message);
13
+
14
+ assert.equal(err.message, message);
15
+ assert.equal(err.stack, undefined);
16
+ });
17
+
18
+ it('should handle passing error as object', () => {
19
+ const err = prettyError({message});
20
+
21
+ assert.equal(err.message, message);
22
+ assert.equal(err.stack, undefined);
23
+ });
24
+
25
+ it('should handle unknown input', () => {
26
+ const err = prettyError(Number.NaN);
27
+
28
+ assert(err.message.length); // non-empty error message
29
+ assert.equal(err.stack, undefined);
30
+ });
31
+
32
+ it('should prepend fileName', () => {
33
+ const err = prettyError({
34
+ message,
35
+ fileName
36
+ });
37
+
38
+ assert(err.message.startsWith(fileName));
39
+ assert.equal(err.stack, undefined);
40
+ });
41
+
42
+ it('should prepend line and column location', () => {
43
+ const err = prettyError({
44
+ message,
45
+ fileName,
46
+ loc: {
47
+ line: 1,
48
+ column: 10
49
+ }
50
+ });
51
+
52
+ assert(err.message.startsWith(`${fileName}:1:10`));
53
+ assert.equal(err.stack, undefined);
54
+ });
55
+
56
+ it('should support providing a codeFrame as stack', () => {
57
+ const err = prettyError({
58
+ message,
59
+ stack,
60
+ codeFrame: codeFrame
61
+ });
62
+
63
+ assert.equal(err.message, message);
64
+ assert.equal(err.stack, codeFrame);
65
+ });
66
+
67
+ it('should support highlightedCodeFrame when opts.color is true', () => {
68
+ let err = prettyError(
69
+ {
70
+ message,
71
+ stack,
72
+ codeFrame: '<not>a code frame</not>',
73
+ highlightedCodeFrame: codeFrame
74
+ },
75
+ {color: true}
76
+ );
77
+
78
+ assert.equal(err.message, message);
79
+ assert.equal(err.stack, codeFrame);
80
+
81
+ err = prettyError(
82
+ {
83
+ message,
84
+ stack,
85
+ codeFrame: codeFrame,
86
+ highlightedCodeFrame: '<not>a code frame</not>'
87
+ },
88
+ {color: false}
89
+ );
90
+
91
+ assert.equal(err.message, message);
92
+ assert.equal(err.stack, codeFrame);
93
+ });
94
+
95
+ it('should support stack', () => {
96
+ const err = prettyError({
97
+ message,
98
+ stack
99
+ });
100
+
101
+ assert.equal(err.message, message);
102
+ assert(err.stack.includes('Line'));
103
+ });
104
+ });