Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e32e51d

Browse files
committed
Initial work to pretty-print CLI
1 parent 107f5b1 commit e32e51d

3 files changed

Lines changed: 208 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import fs from 'fs'
2+
import {getUserCodeFrame} from '../get-user-code-frame'
3+
4+
jest.mock('fs', () => ({
5+
// We setup the contents of a sample file
6+
readFileSync: jest.fn(
7+
() => `
8+
import {screen} from '@testing-library/dom'
9+
it('renders', () => {
10+
document.body.appendChild(
11+
document.createTextNode('Hello world')
12+
)
13+
screen.debug()
14+
expect(screen.getByText('Hello world')).toBeInTheDocument()
15+
})
16+
`,
17+
),
18+
}))
19+
20+
const userStackFrame = 'at somethingWrong (/sample-error/error-example.js:7:14)'
21+
22+
let globalErrorMock
23+
24+
beforeEach(() => {
25+
// Mock global.Error so we can setup our own stack messages
26+
globalErrorMock = jest.spyOn(global, 'Error')
27+
})
28+
29+
afterEach(() => {
30+
global.Error.mockRestore()
31+
})
32+
33+
test('it returns only user code frame when code frames from node_modules are first', () => {
34+
const stack = `Error: Kaboom
35+
at Object.<anonymous> (/sample-error/node_modules/@es2050/console/build/index.js:4:10)
36+
${userStackFrame}
37+
`
38+
globalErrorMock.mockImplementationOnce(() => ({stack}))
39+
const userTrace = getUserCodeFrame(stack)
40+
41+
expect(userTrace).toMatchInlineSnapshot(`
42+
/sample-error/error-example.js:7:14
43+
5 | document.createTextNode('Hello world')
44+
6 | )
45+
> 7 | screen.debug()
46+
| ^
47+
48+
`)
49+
})
50+
51+
test('it returns only user code frame when node code frames are present afterwards', () => {
52+
const stack = `Error: Kaboom
53+
at Object.<anonymous> (/sample-error/node_modules/@es2050/console/build/index.js:4:10)
54+
${userStackFrame}
55+
at Object.<anonymous> (/sample-error/error-example.js:14:1)
56+
at internal/main/run_main_module.js:17:47
57+
`
58+
globalErrorMock.mockImplementationOnce(() => ({stack}))
59+
const userTrace = getUserCodeFrame()
60+
61+
expect(userTrace).toMatchInlineSnapshot(`
62+
/sample-error/error-example.js:7:14
63+
5 | document.createTextNode('Hello world')
64+
6 | )
65+
> 7 | screen.debug()
66+
| ^
67+
68+
`)
69+
})
70+
71+
test("it returns empty string if file from code frame can't be read", () => {
72+
// Make fire read purposely fail
73+
fs.readFileSync.mockImplementationOnce(() => {
74+
throw Error()
75+
})
76+
const stack = `Error: Kaboom
77+
${userStackFrame}
78+
`
79+
globalErrorMock.mockImplementationOnce(() => ({stack}))
80+
81+
expect(getUserCodeFrame(stack)).toEqual('')
82+
})

src/get-user-code-frame.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// We try to load node dependencies
2+
let chalk = null
3+
let readFileSync = null
4+
let codeFrameColumns = null
5+
6+
try {
7+
const nodeRequire = module && module.require
8+
9+
readFileSync = nodeRequire.call(module, 'fs').readFileSync
10+
codeFrameColumns = nodeRequire.call(
11+
module,
12+
'@babel/code-frame',
13+
).codeFrameColumns
14+
chalk = nodeRequire.call(module, 'chalk')
15+
} catch {
16+
// We're in a browser environment
17+
}
18+
19+
// frame has the form "at myMethod (location/to/my/file.js:10:2)"
20+
function getCodeFrame(frame) {
21+
const locationStart = frame.indexOf('(') + 1
22+
const locationEnd = frame.indexOf(')')
23+
const frameLocation = frame.slice(locationStart, locationEnd)
24+
25+
const frameLocationElements = frameLocation.split(':')
26+
const [filename, line, column] = [
27+
frameLocationElements[0],
28+
parseInt(frameLocationElements[1], 10),
29+
parseInt(frameLocationElements[2], 10),
30+
]
31+
32+
let rawFileContents = ''
33+
try {
34+
rawFileContents = readFileSync(filename, 'utf-8')
35+
} catch {
36+
return ''
37+
}
38+
39+
const codeFrame = codeFrameColumns(
40+
rawFileContents,
41+
{
42+
start: {line, column},
43+
},
44+
{
45+
highlightCode: true,
46+
linesBelow: 0,
47+
},
48+
)
49+
return `${chalk.dim(frameLocation)}\n${codeFrame}\n`
50+
}
51+
52+
function getUserCodeFrame() {
53+
// If we couldn't load dependencies, we can't generate the user trace
54+
/* istanbul ignore next */
55+
if (!readFileSync || !codeFrameColumns) {
56+
return ''
57+
}
58+
const err = new Error()
59+
const firstClientCodeFrame = err.stack
60+
.split('\n')
61+
.slice(1) // Remove first line which has the form "Error: TypeError"
62+
.find(frame => !frame.includes('node_modules/')) // Ignore frames from 3rd party libraries
63+
64+
return getCodeFrame(firstClientCodeFrame)
65+
}
66+
67+
export {getUserCodeFrame}

src/pretty-cli.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {getUserCodeFrame} from './get-user-code-frame'
2+
import {getCurrentInstance} from './helpers'
3+
4+
// Regarding `maxLength`:
5+
// This may cause a problem if an ANSI escape sequence is not closed during this length
6+
// Can we just, like, pass all ANSI escape sequences through?
7+
function prettyCLI(instance, maxLength) {
8+
if (!instance) {
9+
instance = getCurrentInstance()
10+
}
11+
if (typeof maxLength !== 'number') {
12+
maxLength =
13+
(typeof process !== 'undefined' && process.env.DEBUG_PRINT_LIMIT) || 7000
14+
}
15+
16+
if (maxLength === 0) {
17+
return ''
18+
}
19+
20+
const contentArr = instance.stdoutArr()
21+
22+
const finalArr = [];
23+
24+
let currSize = 0;
25+
for (let content of contentArr) {
26+
if (currSize >= maxLength) {
27+
break;
28+
}
29+
let maxValLength = maxLength - currSize;
30+
if (maxValLength <= 0) maxValLength = 0;
31+
if (Buffer.isBuffer(content)) {
32+
const bufferSize = Buffer.byteLength(content);
33+
if (bufferSize > maxValLength) {
34+
content = content.slice(0, maxValLength);
35+
}
36+
finalArr.push(content);
37+
currSize += bufferSize;
38+
continue;
39+
}
40+
// content is a string
41+
if (content.length > maxValLength) {
42+
content = content.substr(0, maxValLength);
43+
}
44+
finalArr.push(content);
45+
currSize += content.length;
46+
}
47+
48+
return finalArr;
49+
}
50+
51+
const logCLI = (...args) => {
52+
const userCodeFrame = getUserCodeFrame()
53+
process.stdout.write(prettyCLI(...args))
54+
if (userCodeFrame) {
55+
console.log(`\n\n${userCodeFrame}`)
56+
}
57+
}
58+
59+
export {prettyCLI, logCLI}

0 commit comments

Comments
 (0)