@inquirer/core
Advanced tools
Comparing version 9.0.10 to 9.1.0
@@ -32,16 +32,16 @@ "use strict"; | ||
const node_async_hooks_1 = require("node:async_hooks"); | ||
const type_1 = require("@inquirer/type"); | ||
const mute_stream_1 = __importDefault(require("mute-stream")); | ||
const signal_exit_1 = require("signal-exit"); | ||
const screen_manager_mjs_1 = __importDefault(require('./screen-manager.js')); | ||
const type_1 = require("@inquirer/type"); | ||
const hook_engine_mjs_1 = require('./hook-engine.js'); | ||
const errors_mjs_1 = require('./errors.js'); | ||
function createPrompt(view) { | ||
const prompt = (config, context) => { | ||
var _a, _b; | ||
const prompt = (config, context = {}) => { | ||
var _a; | ||
// Default `input` to stdin | ||
const input = (_a = context === null || context === void 0 ? void 0 : context.input) !== null && _a !== void 0 ? _a : process.stdin; | ||
const { input = process.stdin, signal } = context; | ||
// Add mute capabilities to the output | ||
const output = new mute_stream_1.default(); | ||
output.pipe((_b = context === null || context === void 0 ? void 0 : context.output) !== null && _b !== void 0 ? _b : process.stdout); | ||
output.pipe((_a = context.output) !== null && _a !== void 0 ? _a : process.stdout); | ||
const rl = readline.createInterface({ | ||
@@ -53,67 +53,73 @@ terminal: true, | ||
const screen = new screen_manager_mjs_1.default(rl); | ||
let cancel = () => { }; | ||
const answer = new type_1.CancelablePromise((resolve, reject) => { | ||
(0, hook_engine_mjs_1.withHooks)(rl, (cycle) => { | ||
function checkCursorPos() { | ||
screen.checkCursorPos(); | ||
const cleanups = new Set(); | ||
const { promise, resolve, reject } = type_1.CancelablePromise.withResolver(); | ||
function onExit() { | ||
cleanups.forEach((cleanup) => cleanup()); | ||
screen.done({ clearContent: Boolean(context === null || context === void 0 ? void 0 : context.clearPromptOnDone) }); | ||
output.end(); | ||
} | ||
function fail(error) { | ||
onExit(); | ||
reject(error); | ||
} | ||
if (signal) { | ||
const abort = () => fail(new errors_mjs_1.AbortPromptError({ cause: signal.reason })); | ||
if (signal.aborted) { | ||
abort(); | ||
return promise; | ||
} | ||
signal.addEventListener('abort', abort); | ||
cleanups.add(() => signal.removeEventListener('abort', abort)); | ||
} | ||
(0, hook_engine_mjs_1.withHooks)(rl, (cycle) => { | ||
cleanups.add((0, signal_exit_1.onExit)((code, signal) => { | ||
fail(new errors_mjs_1.ExitPromptError(`User force closed the prompt with ${code} ${signal}`)); | ||
})); | ||
const hooksCleanup = node_async_hooks_1.AsyncResource.bind(() => { | ||
try { | ||
hook_engine_mjs_1.effectScheduler.clearAll(); | ||
} | ||
const removeExitListener = (0, signal_exit_1.onExit)((code, signal) => { | ||
catch (error) { | ||
reject(error); | ||
} | ||
}); | ||
cleanups.add(hooksCleanup); | ||
// Re-renders only happen when the state change; but the readline cursor could change position | ||
// and that also requires a re-render (and a manual one because we mute the streams). | ||
// We set the listener after the initial workLoop to avoid a double render if render triggered | ||
// by a state change sets the cursor to the right position. | ||
const checkCursorPos = () => screen.checkCursorPos(); | ||
rl.input.on('keypress', checkCursorPos); | ||
cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos)); | ||
// The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand | ||
// triggers after the process is done (which happens after timeouts are done triggering.) | ||
// We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared. | ||
rl.on('close', hooksCleanup); | ||
cleanups.add(() => rl.removeListener('close', hooksCleanup)); | ||
function done(value) { | ||
// Delay execution to let time to the hookCleanup functions to registers. | ||
setImmediate(() => { | ||
onExit(); | ||
reject(new errors_mjs_1.ExitPromptError(`User force closed the prompt with ${code} ${signal}`)); | ||
// Finally we resolve our promise | ||
resolve(value); | ||
}); | ||
const hooksCleanup = node_async_hooks_1.AsyncResource.bind(() => { | ||
try { | ||
hook_engine_mjs_1.effectScheduler.clearAll(); | ||
} | ||
catch (error) { | ||
reject(error); | ||
} | ||
}); | ||
function onExit() { | ||
hooksCleanup(); | ||
screen.done({ clearContent: Boolean(context === null || context === void 0 ? void 0 : context.clearPromptOnDone) }); | ||
removeExitListener(); | ||
rl.input.removeListener('keypress', checkCursorPos); | ||
rl.removeListener('close', hooksCleanup); | ||
output.end(); | ||
} | ||
cycle(() => { | ||
try { | ||
const nextView = view(config, done); | ||
const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
screen.render(content, bottomContent); | ||
hook_engine_mjs_1.effectScheduler.run(); | ||
} | ||
cancel = () => { | ||
onExit(); | ||
reject(new errors_mjs_1.CancelPromptError()); | ||
}; | ||
function done(value) { | ||
// Delay execution to let time to the hookCleanup functions to registers. | ||
setImmediate(() => { | ||
onExit(); | ||
// Finally we resolve our promise | ||
resolve(value); | ||
}); | ||
catch (error) { | ||
fail(error); | ||
} | ||
cycle(() => { | ||
try { | ||
const nextView = view(config, done); | ||
const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView; | ||
screen.render(content, bottomContent); | ||
hook_engine_mjs_1.effectScheduler.run(); | ||
} | ||
catch (error) { | ||
onExit(); | ||
reject(error); | ||
} | ||
}); | ||
// Re-renders only happen when the state change; but the readline cursor could change position | ||
// and that also requires a re-render (and a manual one because we mute the streams). | ||
// We set the listener after the initial workLoop to avoid a double render if render triggered | ||
// by a state change sets the cursor to the right position. | ||
rl.input.on('keypress', checkCursorPos); | ||
// The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand | ||
// triggers after the process is done (which happens after timeouts are done triggering.) | ||
// We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared. | ||
rl.on('close', hooksCleanup); | ||
}); | ||
}); | ||
answer.cancel = cancel; | ||
return answer; | ||
promise.cancel = () => { | ||
fail(new errors_mjs_1.CancelPromptError()); | ||
}; | ||
return promise; | ||
}; | ||
return prompt; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ValidationError = exports.HookError = exports.ExitPromptError = exports.CancelPromptError = void 0; | ||
exports.ValidationError = exports.HookError = exports.ExitPromptError = exports.CancelPromptError = exports.AbortPromptError = void 0; | ||
class AbortPromptError extends Error { | ||
constructor(options) { | ||
super(); | ||
Object.defineProperty(this, "name", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'AbortPromptError' | ||
}); | ||
Object.defineProperty(this, "message", { | ||
enumerable: true, | ||
configurable: true, | ||
writable: true, | ||
value: 'Prompt was aborted' | ||
}); | ||
this.cause = options === null || options === void 0 ? void 0 : options.cause; | ||
} | ||
} | ||
exports.AbortPromptError = AbortPromptError; | ||
class CancelPromptError extends Error { | ||
@@ -5,0 +24,0 @@ constructor() { |
@@ -32,5 +32,8 @@ "use strict"; | ||
static isSeparator(choice) { | ||
return Boolean(choice && choice.type === 'separator'); | ||
return Boolean(choice && | ||
typeof choice === 'object' && | ||
'type' in choice && | ||
choice.type === 'separator'); | ||
} | ||
} | ||
exports.Separator = Separator; |
@@ -0,1 +1,8 @@ | ||
export declare class AbortPromptError extends Error { | ||
name: string; | ||
message: string; | ||
constructor(options?: { | ||
cause?: unknown; | ||
}); | ||
} | ||
export declare class CancelPromptError extends Error { | ||
@@ -2,0 +9,0 @@ name: string; |
@@ -9,3 +9,3 @@ /** | ||
constructor(separator?: string); | ||
static isSeparator(choice: undefined | Separator | Record<string, unknown>): choice is Separator; | ||
static isSeparator(choice: unknown): choice is Separator; | ||
} |
{ | ||
"name": "@inquirer/core", | ||
"version": "9.0.10", | ||
"version": "9.1.0", | ||
"engines": { | ||
@@ -61,5 +61,5 @@ "node": ">=18" | ||
"@inquirer/figures": "^1.0.5", | ||
"@inquirer/type": "^1.5.2", | ||
"@inquirer/type": "^1.5.3", | ||
"@types/mute-stream": "^0.0.4", | ||
"@types/node": "^22.1.0", | ||
"@types/node": "^22.5.2", | ||
"@types/wrap-ansi": "^3.0.0", | ||
@@ -76,3 +76,3 @@ "ansi-escapes": "^4.3.2", | ||
"devDependencies": { | ||
"@inquirer/testing": "^2.1.31" | ||
"@inquirer/testing": "^2.1.32" | ||
}, | ||
@@ -101,3 +101,3 @@ "scripts": { | ||
"sideEffects": false, | ||
"gitHead": "273b16eeb96fab346854b9b92257c930d7ca7661" | ||
"gitHead": "0c039599ef88fe9eb804fe083ee386ec906a856f" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
96821
1938
+ Added@inquirer/figures@1.0.5(transitive)
+ Added@inquirer/type@1.5.4(transitive)
- Removed@inquirer/figures@1.0.6(transitive)
- Removed@inquirer/type@1.5.5(transitive)
Updated@inquirer/type@^1.5.3
Updated@types/node@^22.5.2