"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireDefault(require("react")); var _lodash = _interopRequireDefault(require("lodash.throttle")); var _autoBind = _interopRequireDefault(require("auto-bind")); var _logUpdate = _interopRequireDefault(require("log-update")); var _isCi = _interopRequireDefault(require("is-ci")); var _signalExit = _interopRequireDefault(require("signal-exit")); var _reconciler = _interopRequireDefault(require("./reconciler")); var _renderer = _interopRequireDefault(require("./renderer")); var _dom = require("./dom"); var _instances = _interopRequireDefault(require("./instances")); var _App = _interopRequireDefault(require("./components/App")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class Instance { constructor(options) { (0, _autoBind.default)(this); this.options = options; this.rootNode = (0, _dom.createNode)('root'); this.rootNode.onRender = this.onRender; this.renderer = (0, _renderer.default)({ terminalWidth: options.stdout.columns }); this.log = _logUpdate.default.create(options.stdout); this.throttledLog = options.debug ? this.log : (0, _lodash.default)(this.log, { leading: true, trailing: true }); // Ignore last render after unmounting a tree to prevent empty output before exit this.isUnmounted = false; // Store last output to only rerender when needed this.lastOutput = ''; // This variable is used only in debug mode to store full static output // so that it's rerendered every time, not just new static parts, like in non-debug mode this.fullStaticOutput = ''; this.container = _reconciler.default.createContainer(this.rootNode, false, false); this.exitPromise = new Promise((resolve, reject) => { this.resolveExitPromise = resolve; this.rejectExitPromise = reject; }); // Unmount when process exits this.unsubscribeExit = (0, _signalExit.default)(this.unmount, { alwaysLast: false }); } onRender() { if (this.isUnmounted) { return; } const { output, staticOutput } = this.renderer(this.rootNode); // If output isn't empty, it means new children have been added to it const hasStaticOutput = staticOutput && staticOutput !== '\n'; if (this.options.debug) { if (hasStaticOutput) { this.fullStaticOutput += staticOutput; } this.options.stdout.write(this.fullStaticOutput + output); return; } // To ensure static output is cleanly rendered before main output, clear main output first if (hasStaticOutput) { if (!_isCi.default) { this.log.clear(); } this.options.stdout.write(staticOutput); if (!_isCi.default) { this.log(output); } } if (output !== this.lastOutput) { if (!_isCi.default) { this.throttledLog(output); } this.lastOutput = output; } } render(node) { const tree = _react.default.createElement(_App.default, { stdin: this.options.stdin, stdout: this.options.stdout, exitOnCtrlC: this.options.exitOnCtrlC, onExit: this.unmount }, node); _reconciler.default.updateContainer(tree, this.container); } unmount(error) { if (this.isUnmounted) { return; } this.onRender(); this.unsubscribeExit(); // CIs don't handle erasing ansi escapes well, so it's better to // only render last frame of non-static output if (_isCi.default) { this.options.stdout.write(this.lastOutput + '\n'); } else if (!this.options.debug) { this.log.done(); } this.isUnmounted = true; _reconciler.default.updateContainer(null, this.container); _instances.default.delete(this.options.stdout); if (error instanceof Error) { this.rejectExitPromise(error); } else { this.resolveExitPromise(); } } waitUntilExit() { return this.exitPromise; } } exports.default = Instance;