Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@
"dependencies": {
"acorn": "^8.7.1",
"acorn-loose": "^8.3.0",
"chalk": "^4.1.2",
"emphasize": "^4.2.0",
"strip-ansi": "^6.0.1",
"ws": "^7.3.0"
"emphasize": "^4.2.0"
},
"private": true
}
63 changes: 33 additions & 30 deletions src/highlight.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,46 @@
'use strict';

const emphasize = require('emphasize');
const chalk = require('chalk');
const util = require('util');

function makeStyled([start, end]) {
return (s) => `\x1b[${start}m${s}\x1b[${end}m`;
}

const windows = process.platform === 'win32';
const sheet = {
'comment': chalk.gray,
'quote': chalk.gray,
'comment': makeStyled(util.inspect.colors.grey),
'quote': makeStyled(util.inspect.colors.grey),

'keyword': chalk.green,
'addition': chalk.green,
'keyword': makeStyled(util.inspect.colors.green),
'addition': makeStyled(util.inspect.colors.green),

'number': windows ? chalk.yellow : chalk.blue,
'string': chalk.green,
'meta meta-string': chalk.cyan,
'literal': chalk.cyan,
'doctag': chalk.cyan,
'regexp': chalk.cyan,
'number': makeStyled(util.inspect.colors.yellow),
'string': makeStyled(util.inspect.colors.green),
'meta meta-string': makeStyled(util.inspect.colors.cyan),
'literal': makeStyled(util.inspect.colors.cyan),
'doctag': makeStyled(util.inspect.colors.cyan),
'regexp': makeStyled(util.inspect.colors.cyan),

'attribute': undefined,
'attr': undefined,
'variable': chalk.yellow,
'template-variable': chalk.yellow,
'class title': chalk.yellow,
'type': chalk.yellow,

'symbol': chalk.magenta,
'bullet': chalk.magenta,
'subst': chalk.magenta,
'meta': chalk.magenta,
'meta keyword': chalk.magenta,
'link': chalk.magenta,

'built_in': chalk.cyan,
'deletion': chalk.red,

'emphasis': chalk.italic,
'strong': chalk.bold,
'formula': chalk.inverse,
'variable': makeStyled(util.inspect.colors.yellow),
'template-variable': makeStyled(util.inspect.colors.yellow),
'class title': makeStyled(util.inspect.colors.yellow),
'type': makeStyled(util.inspect.colors.yellow),

'symbol': makeStyled(util.inspect.colors.magenta),
'bullet': makeStyled(util.inspect.colors.magenta),
'subst': makeStyled(util.inspect.colors.magenta),
'meta': makeStyled(util.inspect.colors.magenta),
'meta keyword': makeStyled(util.inspect.colors.magenta),
'link': makeStyled(util.inspect.colors.magenta),

'built_in': makeStyled(util.inspect.colors.cyan),
'deletion': makeStyled(util.inspect.colors.red),

'emphasis': makeStyled(util.inspect.colors.italic),
'strong': makeStyled(util.inspect.colors.bold),
'formula': makeStyled(util.inspect.colors.inverse),
};

module.exports = (s) =>
Expand Down
81 changes: 37 additions & 44 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,43 @@
'use strict';

const { createInterface, clearScreenDown } = require('readline');
const stripAnsi = require('strip-ansi');
const { spawn } = require('child_process');
const acorn = require('acorn-loose');
const chalk = require('chalk');
const { Session } = require('./inspector');
const { isIdentifier, strEscape, underlineIgnoreANSI } = require('./util');
const highlight = require('./highlight');
const getHistory = require('./history');
const util = require('util');

require('./stub.js')

function stripAnsi(text) {
const ansiEscape = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
return text.replace(ansiEscape, '');
}

function makePrompt(i) {
return chalk.green(`In [${chalk.bold(i)}]: `);
return util.styleText('green', `In [${util.styleText('bold', '' + i)}]: `);
}

function makePromptOut(inspected, i) {
if (/[\r\n\u2028\u2029]/u.test(inspected)) {
return '';
}
return chalk.red(`Out[${chalk.bold(i)}]: `);
return util.styleText('red', `Out[${util.styleText('bold', '' + i)}]: `);
}

function promptLength(i) {
return `In [${i}]: `.length;
}

async function start(wsUrl) {
const session = await Session.create(wsUrl);
async function start() {
const session = await Session.create();

const { context } = await new Promise((resolve) => {
session.once('Runtime.executionContextCreated', resolve);
session.post('Runtime.enable');
});

session.post('Runtime.enable');
const [{ context }] = await Session.once(session, 'Runtime.executionContextCreated');
const { result: remoteGlobal } = await session.post('Runtime.evaluate', {
expression: 'globalThis',
});
Expand Down Expand Up @@ -113,8 +121,8 @@ async function start(wsUrl) {

// Convert inspection target to object.
if (evaluateResult.result.type !== 'object'
&& evaluateResult.result.type !== 'undefined'
&& evaluateResult.result.subtype !== 'null') {
&& evaluateResult.result.type !== 'undefined'
&& evaluateResult.result.subtype !== 'null') {
evaluateResult = await evaluate(`Object(${expr})`, true);
if (evaluateResult.exceptionDetails) {
return undefined;
Expand Down Expand Up @@ -205,19 +213,28 @@ async function start(wsUrl) {
}
return callFunctionOn(
`function inspect(v) {
const i = util.inspect(v, {
let i = util.inspect(v, {
colors: false,
breakLength: Infinity,
compact: true,
maxArrayLength: 10,
depth: 1,
});
return i.split('\\n')[0].trim();
return i;
}`,
[result],
);
})
.then(({ result }) => result.value)
.then(({ result }) => {
let val = result.value;
let noAnsi = stripAnsi(val);
let length = process.stdout.columns - promptLength(promptIndex) - 2;
if (noAnsi.length > length) {
noAnsi = noAnsi.slice(0, length - 3) + '...';
}

return noAnsi;
})
.catch(() => undefined);

const rl = createInterface({
Expand Down Expand Up @@ -371,10 +388,8 @@ async function start(wsUrl) {
process.stdout.cursorTo(promptLength(promptIndex) + rl.cursor);

if (inspectedLine !== '') {
Promise.all([
completeLine(inspectedLine),
getPreview(inspectedLine),
])
completeLine(inspectedLine)
.then(async(completion) => [completion, await getPreview(inspectedLine + (completion?.completions[0] || ''))])
.then(([completion, preview]) => {
if (rl.line !== inspectedLine) {
return;
Expand All @@ -386,18 +401,18 @@ async function start(wsUrl) {
([completionCache] = completion.completions);
}
process.stdout.cursorTo(promptLength(promptIndex) + rl.line.length);
process.stdout.write(chalk.grey(completion.completions[0]));
process.stdout.write(util.styleText('grey', completion.completions[0]));
rows += countLines(completion.completions[0]) - 1;
}
if (preview) {
process.stdout.write(chalk.grey(`\nOut[${promptIndex}]: ${preview}\n`));
process.stdout.write(util.styleText('grey', `\nOut[${promptIndex}]: ${preview}\n`));
rows += countLines(preview) + 1;
}

process.stdout.cursorTo(promptLength(promptIndex) + rl.cursor);
process.stdout.moveCursor(0, -rows);
})
.catch(() => {});
.catch(() => { });
}
};

Expand All @@ -420,7 +435,6 @@ Prototype REPL - https://github.com/nodejs/repl
'function inspect(uncaught, line, value) { return globalThis[Symbol.for("nodejs.repl.updateInspect")](uncaught, line, value); }',
[{ value: uncaught }, { value: promptIndex }, result],
);

process.stdout.write(`${makePromptOut(inspected.value, promptIndex)}${uncaught ? 'Uncaught ' : ''}${inspected.value}\n\n`);

promptIndex += 1;
Expand All @@ -438,25 +452,4 @@ Prototype REPL - https://github.com/nodejs/repl
}
}

const child = spawn(process.execPath, [
'--inspect-publish-uid=http',
...process.execArgv,
require.resolve('./stub.js'),
...process.argv,
], {
cwd: process.cwd(),
windowsHide: true,
});

child.stdout.on('data', (data) => {
process.stdout.write(data);
});

child.stderr.on('data', (data) => {
const s = data.toString();
if (s.startsWith('__DEBUGGER_URL__')) {
start(s.split(' ')[1]);
} else if (s !== 'Debugger attached.\n') {
process.stderr.write(data);
}
});
start();
79 changes: 45 additions & 34 deletions src/inspector.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,70 @@
'use strict';

const { EventEmitter } = require('events');
const WebSocket = require('ws');
const inspector = require('inspector');

const events = [
'inspectorNotification',
'Runtime.executionContextCreated',
'Runtime.executionContextDestroyed',
'Runtime.executionContextsCleared',
'Runtime.exceptionThrown',
'Runtime.exceptionRevoked',
'Runtime.consoleAPICalled',
'Runtime.inspectRequested',
'Debugger.scriptParsed',
'Debugger.scriptFailedToParse',
'Debugger.breakpointResolved',
'Debugger.paused',
'Debugger.resumed',
'Console.messageAdded',
'Profiler.consoleProfileStarted',
'Profiler.consoleProfileFinished',
'HeapProfiler.addHeapSnapshotChunk',
'HeapProfiler.resetProfiles',
'HeapProfiler.reportHeapSnapshotProgress',
'HeapProfiler.lastSeenObjectId',
'HeapProfiler.heapStatsUpdate',
'NodeTracing.dataCollected',
'NodeTracing.tracingComplete',
'NodeWorker.attachedToWorker',
'NodeWorker.detachedFromWorker',
'NodeWorker.receivedMessageFromWorker',
'NodeRuntime.waitingForDisconnect'
];

class Session extends EventEmitter {
constructor(url) {
super();

this.ws = new WebSocket(url);
this.ws.on('message', (d) => {
this.onMessage(d);
});
this.ws.on('open', () => {
this.emit('open');
});
this.session = new inspector.Session();
this.session.connect();

this.messageCounter = 0;
this.messages = new Map();
for (const event of events) {
this.session.on(event, this.onMessage.bind(this));
}
}

static create(url) {
return new Promise((resolve) => {
const s = new Session(url);
s.once('open', () => resolve(s));
});
return new Session(url);
}

onMessage(d) {
const { id, method, params, result, error } = JSON.parse(d);
const { method, params } = d;
if (method) {
this.emit(method, params);
} else {
const { resolve, reject } = this.messages.get(id);
this.messages.delete(id);
if (error) {
const e = new Error(error.message);
e.code = error.code;
reject(e);
} else {
resolve(result);
}
}
}

post(method, params) {
return new Promise((resolve, reject) => {
const id = this.messageCounter;
this.messageCounter += 1;
const message = {
method,
params,
id,
};
this.messages.set(id, { resolve, reject });
this.ws.send(JSON.stringify(message));
this.session.post(method, params, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
}
Expand Down
14 changes: 1 addition & 13 deletions src/stub.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,8 @@

const Module = require('module');
const path = require('path');
const inspector = require('inspector');
const util = require('util');

inspector.open(0, true);
process.stderr.write(`__DEBUGGER_URL__ ${inspector.url()}`);

if (process.platform !== 'win32') {
util.inspect.styles.number = 'blue';
util.inspect.styles.bigint = 'blue';
}

Module.builtinModules
.filter((x) => !/^_|\//.test(x))
.forEach((name) => {
Expand Down Expand Up @@ -97,7 +88,4 @@ globalThis[Symbol.for('nodejs.repl.updateInspect')] = (uncaught, line, value) =>
colors: true,
showProxy: true,
});
};

// keep process alive using stdin
process.stdin.on('data', () => {});
};