hyper/app/session.ts

208 lines
5.2 KiB
TypeScript
Raw Normal View History

2019-11-28 05:17:01 -09:00
import {EventEmitter} from 'events';
import {StringDecoder} from 'string_decoder';
import defaultShell from 'default-shell';
import {getDecoratedEnv} from './plugins';
2020-01-02 05:44:11 -09:00
import {productName, version} from './package.json';
2019-11-28 05:17:01 -09:00
import * as config from './config';
2020-01-02 05:44:11 -09:00
import {IPty, IWindowsPtyForkOptions, spawn as npSpawn} from 'node-pty';
2016-06-30 22:01:04 -08:00
const createNodePtyError = () =>
new Error(
'`node-pty` failed to load. Typically this means that it was built incorrectly. Please check the `readme.md` to more info.'
);
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
2020-01-02 05:44:11 -09:00
let spawn: typeof npSpawn;
2016-07-13 13:06:05 -08:00
try {
spawn = require('node-pty').spawn;
2016-07-13 13:06:05 -08:00
} catch (err) {
2017-01-15 06:22:07 -09:00
throw createNodePtyError();
2016-07-13 13:06:05 -08:00
}
const envFromConfig = config.getConfig().env || {};
2019-08-01 18:12:46 -08:00
const useConpty = config.getConfig().useConpty;
// Max duration to batch session data before sending it to the renderer process.
const BATCH_DURATION_MS = 16;
// Max size of a session data batch. Note that this value can be exceeded by ~4k
// (chunk sizes seem to be 4k at the most)
const BATCH_MAX_SIZE = 200 * 1024;
// Data coming from the pty is sent to the renderer process for further
// vt parsing and rendering. This class batches data to minimize the number of
// IPC calls. It also reduces GC pressure and CPU cost: each chunk is prefixed
// with the window ID which is then stripped on the renderer process and this
// overhead is reduced with batching.
class DataBatcher extends EventEmitter {
2020-01-02 05:44:11 -09:00
uid: string;
decoder: StringDecoder;
data!: string;
timeout!: NodeJS.Timeout | null;
constructor(uid: string) {
super();
this.uid = uid;
this.decoder = new StringDecoder('utf8');
this.reset();
}
reset() {
this.data = this.uid;
this.timeout = null;
}
2020-01-02 05:44:11 -09:00
write(chunk: Buffer) {
if (this.data.length + chunk.length >= BATCH_MAX_SIZE) {
// We've reached the max batch size. Flush it and start another one
if (this.timeout) {
clearTimeout(this.timeout);
this.timeout = null;
}
this.flush();
}
this.data += this.decoder.write(chunk);
if (!this.timeout) {
this.timeout = setTimeout(() => this.flush(), BATCH_DURATION_MS);
}
}
flush() {
// Reset before emitting to allow for potential reentrancy
const data = this.data;
this.reset();
this.emit('flush', data);
}
}
2020-01-02 05:44:11 -09:00
interface SessionOptions {
uid: string;
rows: number;
cols: number;
cwd: string;
shell: string;
shellArgs: string[];
}
2019-11-28 05:17:01 -09:00
export default class Session extends EventEmitter {
2020-01-02 05:44:11 -09:00
pty: IPty | null;
batcher: DataBatcher | null;
shell: string | null;
ended: boolean;
constructor(options: SessionOptions) {
2016-06-30 22:01:04 -08:00
super();
this.pty = null;
this.batcher = null;
this.shell = null;
this.ended = false;
this.init(options);
}
2020-01-02 05:44:11 -09:00
init({uid, rows, cols: columns, cwd, shell, shellArgs}: SessionOptions) {
const osLocale = require('os-locale') as typeof import('os-locale');
const baseEnv = Object.assign(
{},
process.env,
{
2019-11-28 05:17:01 -09:00
LANG: `${osLocale.sync().replace(/-/, '_')}.UTF-8`,
TERM: 'xterm-256color',
COLORTERM: 'truecolor',
TERM_PROGRAM: productName,
TERM_PROGRAM_VERSION: version
},
envFromConfig
);
// Electron has a default value for process.env.GOOGLE_API_KEY
// We don't want to leak this to the shell
// See https://github.com/zeit/hyper/issues/696
if (baseEnv.GOOGLE_API_KEY && process.env.GOOGLE_API_KEY === baseEnv.GOOGLE_API_KEY) {
delete baseEnv.GOOGLE_API_KEY;
}
const defaultShellArgs = ['--login'];
2020-01-02 05:44:11 -09:00
const options: IWindowsPtyForkOptions = {
2019-08-01 18:12:46 -08:00
cols: columns,
rows,
cwd,
env: getDecoratedEnv(baseEnv)
};
// if config do not set the useConpty, it will be judged by the node-pty
if (typeof useConpty === 'boolean') {
options.useConpty = useConpty;
2019-08-01 18:12:46 -08:00
}
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
try {
2019-08-01 18:12:46 -08:00
this.pty = spawn(shell || defaultShell, shellArgs || defaultShellArgs, options);
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
} catch (err) {
if (/is not a function/.test(err.message)) {
2017-01-15 06:22:07 -09:00
throw createNodePtyError();
Add Windows support and first-class Linux support (#946) * `child_pty` => `pty.js` * Create a frameless window on Windows and Linux * Add a brand new UI for Linux and Windows :nail_care: * [Windows] Fix plugin installation * [Windows] Fix the `build` script * [Windows] Add a bigger `icon.ico` * [Mac] Add `WebKitAppRegion: drag` when running on macOS * Fix code style :thinking: * Add `appveyor.yml` * Fix code style (again) * [Windows] Fix AppVeyor's `install` script * [Windows] Try a new AppVeyor config * [Windows] Set the binary path so Spectron can run the tests * [Windows] Try to build on x64 * Try again to build on x64 * Try one more time :weary: * Throw an error to indicate that `pty.js` was built incorrectly * [Win/Linux] Add `display: hidden` to <Tabs /> if tabs.length === 1 * [Win/Linux] Reorganize SVGs – via @CodeTheory * [Win/Linux] Fix the hamburger menu height * Make the SVGs look better with `shape-rendering: crispEdges;` * [Win/Linux] Add config options for the window controls and the :hamburger: menu * Add `electron-squirrel-startup` dependency * [Win] Handle Squirrel commands * [Win/Linux] Fix default color for the :hamburger: and window controls – via @CodeTheory * [Win/Linux] Add some padding - via @CodeTheory * [Win/Linux] Add hover states – via @CodeTheory * [Win] Fix empty window/tab titles * [Win] Fix opening Preferences (#978) * [Win] Fix opening Preferences * Update ui.js * Update ui.js * Enhance messages and default editor * [Win] Add dependency instructions to the README.md [skip ci] * Fix code style * [Win/Linux] Check the number of open windows before quitting the app
2016-11-11 08:18:04 -09:00
} else {
throw err;
}
}
2016-06-30 22:01:04 -08:00
this.batcher = new DataBatcher(uid);
this.pty.onData(chunk => {
if (this.ended) {
return;
}
2020-01-02 05:44:11 -09:00
this.batcher?.write(chunk as any);
});
this.batcher.on('flush', data => {
this.emit('data', data);
2016-06-30 22:01:04 -08:00
});
this.pty.onExit(() => {
2016-06-30 22:01:04 -08:00
if (!this.ended) {
this.ended = true;
this.emit('exit');
}
});
2016-07-23 16:57:47 -08:00
this.shell = shell || defaultShell;
2016-06-30 22:01:04 -08:00
}
exit() {
2016-06-30 22:01:04 -08:00
this.destroy();
}
2020-01-02 05:44:11 -09:00
write(data: string) {
if (this.pty) {
this.pty.write(data);
} else {
console.warn('Warning: Attempted to write to a session with no pty');
}
2016-06-30 22:01:04 -08:00
}
2020-01-02 05:44:11 -09:00
resize({cols, rows}: {cols: number; rows: number}) {
if (this.pty) {
try {
this.pty.resize(cols, rows);
} catch (err) {
console.error(err.stack);
}
} else {
console.warn('Warning: Attempted to resize a session with no pty');
2016-06-30 22:01:04 -08:00
}
}
destroy() {
if (this.pty) {
try {
this.pty.kill();
} catch (err) {
console.error('exit error', err.stack);
}
} else {
console.warn('Warning: Attempted to destroy a session with no pty');
2016-07-03 12:35:45 -08:00
}
2016-06-30 22:01:04 -08:00
this.emit('exit');
this.ended = true;
}
2019-11-28 05:17:01 -09:00
}