I am making a CLI using inquirer in nodejs.
So in Every choice list I have to give Exit choice so if user want to exit he/she can easily Exit.
So I have to write Exit again and again to avoid that problem I made a Exit.js file and move Exit code there so I can use code again and again.
Exit.js
const executeQuery = require("../executeQuery");
function WantToExit() {
inquirer
.prompt([
{
name: "moreQuery",
type: "confirm",
message: "Want to do anything else?",
},
])
.then((answer) => {
if (answer.moreQuery) return executeQuery();
});
}
module.exports = WantToExit;
and My executeQuery Code look like this
ExecuteQuery.js
const wantToExit = require("../Exit");
const Science = require("../Science");
function executetQuery() {
inquirer
.prompt([
{
type: "list",
name: "cmsType",
message: " Select Subject Options ",
default: false,
choices: ["Science", "Maths", "English", "Exit"],
},
])
.then((answers) => {
if (answers.cmsType === "Science") {
Science();
} else if (answers.cmsType === "Exit") {
wantToExit();
}
});
}
module.exports = executetQuery;
when I select Exit from executeQuery option and press Y option I am getting this error from Exit.js file
if (answer.moreQuery) return executeQuery();
^
TypeError: executeQuery is not a function
at /home/admin/SchoolProject/src/Exit/index.js:13:36
Your approach has issues because it has produced a cyclic dependency of modules. You have "required" wantToExit in ExecuteQuery.js and also "required" executetQuery() in Exit.js
What I believe you want to achieve is to keep asking user his preferred subject and then do something based on his/her choice until a user selects Exit.
I would suggest to use a while loop in ExecuteQuery.js for the main prompt and use a boolean flag to check if user wants to exit.
const wantToExit = require("../Exit");
const Science = require("../Science");
function executetQuery() {
let toStop = false;
// use a while loop
while(!toStop) {
inquirer
.prompt([
{
type: "list",
name: "cmsType",
message: " Select Subject Options ",
default: false,
choices: ["Science", "Maths", "English", "Exit"],
},
])
.then(async (answers) => {
if (answers.cmsType === "Science") {
// you can also set toStop = true here if you want to
// stop after first iteration
Science();
} else if (answers.cmsType === "Exit") {
// wantToExit() now returns a boolean flag
toStop = await wantToExit();
}
});
}
}
module.exports = executetQuery;
and your Exit.js should be like
function WantToExit() {
inquirer
.prompt([
{
name: "moreQuery",
type: "confirm",
message: "Want to do anything else?",
},
])
.then((answer) => {
return !answer.moreQuery;
});
}
module.exports = WantToExit;
This is a scenario of circular dependency. A requires B, B requires A and so on. To get it working, you'll have to modify the module.exports.
In Exit.js file, change module.exports=WantToExit to module.exports.WantToExit = WantToExit and require it as const {WantToExit} =require('./Exit.js') in ExecuteQuery.js file.
Similiary, module.exports.ExecuteQuery=ExecuteQuery and require as const {ExecuteQuery} =require('./ExecuteQuery.js')
My guidance would be to learn RXJS and observables into this somehow.
Also i think (yield* ) might work in strict mode not sure, i wanted to not it because this is more a suggestion to play with and look into
Generator Functions* Exploring ES6 © 2015 - 2018 Axel Rauschmayer (cover by Fran Caye)
RXJS Guide Observable
const { Observable } = require("rxjs");
async function* wantToExit() {
(yield* await inquirer
.prompt([
{
name: "moreQuery",
type: "confirm",
message: "Want to do anything else?",
},
])
.then(answer => answer.moreQuery)
);
}
const executeQuery = new Observable(subscriber => {
inquirer.prompt([
{
type: "list",
name: "cmsType",
message: " Select Subject Options ",
default: false,
choices: ["Science", "Maths", "English", "Exit"],
},
]).then((answers) => {
if (answers.cmsType === "Science") {
subscriber.next(answers.cmsType);
} else if (answers.cmsType === "Exit") {
let doWeExit = await wantToExit().next();
if (doWeExit === ADD_SOMETHING_NO) {
executeQuery.subscribe(userResponse => userResponse);
} else {
console.log('Adios!');
return false;
}
}
});
});
module.exports = { executeQuery };
On a new page you could than do. Or you could just use it right under the function declaration. Hope this vaguely helps to the next step.
const {executeQuery} = require('{INCLUDE YOUR FILEPATH}');
executeQuery.subscribe(userResponse => {
if(userResponse === 'Science') science();
console.log(userResponse);
});
Related
Any help with this would be greatly appreciated.
I have two youtrack projects 'Support' and 'Bug DB'.
In the support project I have a workflow rule that a specific state transition to a state 'Awaiting Fix' automatically generates an issue in the 'Bug DB' project and links back to the original issue with an 'affects helpdesk log' link type.
In the 'Bug DB' project I want to add a rule to automatically update the state of the issue in the 'Support' project to 'Action Required' when a specific state transition occurs.
I can manage the state transition parts but I have two outstanding problems in my code for the 'Bug DB' project:
Guard condition
Currently I have a function that (I hope) just checks that a link exists - ideally this should check that the link specifically points to an issue in the 'Support' project.
const issue_0 = ctx.issue;
...
function hasLinkedSupportIssue() {
//I think this is equivalent to a C# .Any()
return issue_0.links.added
}
Update
To apply the new state I am iterating the linked issues (because I don't know how to get a specific one)
issue_0.links['affects helpdesk log'].added.forEach(updateState)
and I then have a function that will do the actual update
const updateState = function(supportIssue) {
//Check here that this is a 'Support' issue
//because my guard is not sufficient
if(supportIssue.project.key === 'Support') {
//not sure I can do assignment like this
supportIssue.state = ctx.SupportState.ActionRequired;
}
}
....
requirements: {
BugState: {
name: "State",
type: entities.State.fieldType,
InProgress: {name: "In Progress"},
Closed: {name: "Closed"}
},
SupportState: {
name: "State",
type: entities.State.fieldType,
AwaitingFix: {name: "Awaiting Fix"},
ActionRequired: {name: "Action Required"}
}
For anyone trying to do this, this was the eventual solution I came up with:
var entities = require('#jetbrains/youtrack-scripting-api/entities');
var workflow = require('#jetbrains/youtrack-scripting-api/workflow');
exports.rule = entities.Issue.onChange({
title: 'Support_bug_resolved',
guard: (ctx) => {
const logger = new Logger(ctx.traceEnabled);
const thisIssue = ctx.issue;
function checkFieldState() {
//check that state is changing from closed to resolved
return thisIssue.fields.isChanged(ctx.BugState) && thisIssue.fields.was(ctx.BugState, ctx.BugState.Resolved) && thisIssue.fields.becomes(ctx.BugState, ctx.BugState.Closed);
}
function hasLinkedSupportIssue() {
//Check we have a linked issue from the support project
return thisIssue.links["helpdesk log affected by"] != 0;
}
function checkFieldFn() {
var check = checkFieldState();
var hasLink = hasLinkedSupportIssue();
return check && hasLink;
};
try {
return checkFieldFn();
} catch (err) {
logger.error(err.message);
}
},
action: function(ctx) {
const logger = new Logger(ctx.traceEnabled);
const thisIssue = ctx.issue;
//Get the first linked support issue - we assume there is always only one
const supportIssue = thisIssue.links['helpdesk log affected by'].first();
try {
//If the current state of the linked issue is 'Awaitingfix', make it 'ActionRequired'
if(supportIssue.State.name == "AwaitingFix") {
supportIssue.State = ctx.SupportState.ActionRequired;
}
} catch(err) {
logger.error(err.message);
}
},
requirements: {
BugState: {
name: "State",
type: entities.State.fieldType,
Resolved: {name: "Resolved"},
Closed: {name: "Closed"}
},
SupportState: {
name: "State",
type: entities.State.fieldType,
AwaitingFix: {name: "AwaitingFix"},
ActionRequired: {name: "ActionRequired"}
}
}
});
function Logger(useDebug = true) {
return {
log: (...args) => useDebug && console.log(...args),
warn: (...args) => useDebug && console.warn(...args),
error: (...args) => useDebug && console.error(...args)
}
}
I recently have spotted jscodeshift and leveraging it for refactoring.
But I can't find a way to add await before the node that I am seeking.
// AS-IS
userEvent.click(a);
await userEvent.click(b);
// TO-BE
await userEvent.click(a);
await userEvent.click(b);
This is how I query userEvent.click
const getFunctionCall = (obj:string, prop:string) => {
return source.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: {
name: obj
},
property: {
name:prop
}
}
})
}
const clicks = getFunctionCall('userEvent', 'click');
clicks.forEach(i=> {
if (i.parentPath.value.type !== 'AwaitExpression') {
// code goes here
}
})
How can I add await before userEvent and let the rest code stay same in this case?
I checked out the document to find out how to build a statement or expression, but it wasn't understandable for me as I've just started to use this. I would appreciate if you can introduce any good materials for this library.
Thanks in advance!
It is simple, you need to wrap the existing CallExpression inside an await expression like this:
// Press ctrl+space for code completion
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
const body = root.get().value.program.body;
root.find(j.CallExpression, {
callee: {
object: {
name: "userEvent"
},
property: {
name: "click"
}
}
}).filter(path => {
return path.value.arguments[0].name === "a" ;
}).replaceWith(path => {
return j.awaitExpression(path.value)
});
return root.toSource();
}
I'm playing around learning XState and wanted to include an action in a machine that would just log the current state to console.
Defining a simple example machine like so, how would I go about this? Also note the questions in the comments in the code.
import { createMachine, interpret } from "xstate"
const sm = createMachine({
initial: 'foo',
states: {
foo: {
entry: 'logState', // Can I only reference an action by string?
// Or can I add arguments here somehow?
on: {
TOGGLE: {target: 'bar'}
}
},
bar: {
entry: 'logState',
on: {
TOGGLE: {target: 'foo'}
}
}
}
},
{
actions: {
logState(/* What arguments can go here? */) => {
// What do I do here?
}
}
});
I know that actions are called with context and event as arguments but I don't see a way to get the current state from either of those. Am I missing something here?
For a simple use case like yours, you could try recording the state on transition.
let currentState;
const service = interpret(machine).onTransition(state => {
if (state.value != currentState) {
// TODO: terminate timer if any and start a new one
currentState = state.value;
}
});
Then use the value in your actions.
See more here: https://github.com/statelyai/xstate/discussions/1294
Actions receive three arguments - context, event and meta. meta have property state, which is current state.
import { createMachine } from "xstate";
let metaDemo = createMachine(
{
id: "meta-demo",
initial: "ping",
states: {
ping: {
entry: ["logStateValues"],
after: { TIMEOUT: "pong" },
},
pong: {
entry: ["logStateValues"],
after: { TIMEOUT: "ping" },
},
},
},
{
delays: {
TIMEOUT: 3000,
},
actions: {
logStateValues(ctx, event, meta) {
if (meta.state.matches("ping")) {
console.log("It's PING!");
} else if (meta.state.matches("pong")) {
console.log("And now it's PONG");
} else {
console.log(
`This is not supposed to happen. State is: ${meta.state
.toStrings()
.join(".")}`
);
}
},
},
}
);
Goal: Wait custom suggestions from a fake server response.
Problem: I cannot understand how I can tell to Monaco editor completion items provider to wait for async suggestions.
Playground example:
Go to playground
Paste the following code
console.log("1. Instantiate a standalone code editor");
const language = "json"
const content = `{\n\t\"dependencies\": {\n\t\t\n\t}\n}\n`
const options = {
value: content,
language: language,
tabSize: 2
}
const standaloneCodeEditor = monaco.editor.create(document.getElementById("container"), options);
const sce = standaloneCodeEditor;
console.log("2. Declare function to provide a completion items")
function provideCompletionItems(model, position) {
console.log("Invoking function 'provideCompletionItems'")
var textUntilPosition = model.getValueInRange({ startLineNumber: 1, startColumn: 1, endLineNumber: position.lineNumber, endColumn: position.column });
console.log("textUntilPosition:", textUntilPosition)
var match = textUntilPosition.match(/"dependencies"\s*:\s*\{\s*("[^"]*"\s*:\s*"[^"]*"\s*,\s*)*([^"]*)?$/);
console.log("match:", match)
if (!match) {
return { suggestions: [] };
}
var word = model.getWordUntilPosition(position);
console.log("word:", word)
var range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
};
console.log("range:", range)
const mock_serverResponse = [
{
label: '"lodash"',
kind: monaco.languages.CompletionItemKind.Function,
documentation: "The Lodash library exported as Node.js modules.",
insertText: '"lodash": "*"',
range: range
},
{
label: '"lodash111"',
kind: monaco.languages.CompletionItemKind.Function,
documentation: "The Lodash111 library exported as Node.js modules.",
insertText: '"lodash111": "*"',
range: range
},
{
label: '"express"',
kind: monaco.languages.CompletionItemKind.Function,
documentation: "Fast, unopinionated, minimalist web framework",
insertText: '"express": "*"',
range: range
},
{
label: '"mkdirp"',
kind: monaco.languages.CompletionItemKind.Function,
documentation: "Recursively mkdir, like <code>mkdir -p</code>",
insertText: '"mkdirp": "*"',
range: range
},
{
label: '"my-third-party-library"',
kind: monaco.languages.CompletionItemKind.Function,
documentation: "Describe your library here",
insertText: '"${1:my-third-party-library}": "${2:1.2.3}"',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: range
}
]
let myCustomSuggestions = [];
console.log("myCustomSuggestions:",myCustomSuggestions)
setTimeout(() => {
const myPromise = Promise.resolve(mock_serverResponse)
console.log("myPromise:",myPromise)
myPromise
.then(response => {
console.log("response:", response)
response.forEach(e => myCustomSuggestions.push(e))
console.log("myCustomSuggestions:",myCustomSuggestions)
return {
suggestions: myCustomSuggestions
}
})
.catch(err => console.error(err))
}, 2000)
}
console.log("3. Register completion items provider")
const completionItemProvider = monaco.languages.registerCompletionItemProvider(language, {
provideCompletionItems: () => provideCompletionItems(sce.getModel(), sce.getPosition())
});
Display web developer console (to see log messages)
Run the program
Type into brackets the word lodash
See web developer console
Notice that no suggestions are displayed
it's easy to solve this problem.
first of all, don't show anything when need competion, or show something you like.And you need to know how to show suggestions actively. yes use this code:
isReady=true;
yourEditor.trigger('anything', 'editor.action.triggerSuggest', {});
now we can show suggestion in any time.
Now we get it!
let yourSuggestions = [];
monaco.languages.registerCompletionItemProvider('vb', {
provideCompletionItems: function (model, position) {
// show some waitting message here
if(isReady){
return {suggestion: yourSuggestions}
}
requestSuggestions();
return {suggestions:[]};
}
});
function requestSuggestions(){
// do something you need
$.ajax({
//.....
success(data){
isReady = true;
yourSuggestions = data.suggestions;
yourEditor.trigger('anything', 'editor.action.triggerSuggest', {});
}
})
}
this just a method and you can prove it
provideCompletionItems method support a Promise return result. monaco.d.ts
function handleOrder(data) {
if (data.payment_type === 'VISA') {
handleVisaPayment()
}
if (data.payment_type === 'VISA') {
handleMastercardPayment()
}
if (data.payment_type === 'PayPal') {
handlePayPalPayment()
}
if (data.shipping === 'Express') {
handleExpressShipping()
}
if (data.shipping === 'Standard') {
handleStandardShipping()
}
}
Is there a better way to write this function especially by following best practices?
You can create an array data structure that will hold the value you want to compare and the function that you want to execute in that case. That will simplify your code and keep it manageable:
let allowedValues = [{
"value": "VISA",
"handler": handleVisaPayment
},
{
"value": "PayPal",
"handler": handlePayPalPayment
},
{
"value": "Express",
"handler": handleExpressShipping
},
{
"value": "Standard",
"handler": handleStandardShipping
}
]
function handleOrder(data) {
let matchedOrder = allowedValues.find(({
value
}) => value === data.payment_type);
if (matchedOrder) {
matchedOrder.handler();
}
}
You can even create a object mapping for those values and operations:
let allowedValues = {
"VISA": handleVisaPayment,
"PayPal": handlePayPalPayment,
"Express": handleExpressShipping,
"Standard": handleStandardShipping
}
function handleOrder(data) {
if (allowedValues[data.payment_type]) {
allowedValues[data.payment_type]();
}
}
First of all, you are executing the same IF statement twice in a row.
if (data.payment_type === 'VISA') {
handleVisaPayment
}
if (data.payment_type === 'VISA') {
handleMastercardPayment
}
I imagine it would be more logical if your second IF statement was like this:
if (data.payment_type === 'Mastercard') {
handleMastercardPayment
}
This looks like a simple copy-paste mistake, but I think it's worth pointing out.
Secondly, you might want to reorganize your code using a switch statement.
Ex:
switch (data.payment_type) {
case 'VISA':
handleVisaPayment
break;
case 'Mastercard':
handleMastercardPayment
break;
case 'PayPal':
handlePayPalPayment
break;
}
switch (data.shipping) {
case 'Express':
handleExpressShipping
break;
case 'Standard':
handleStandardShipping
break;
}
This should make your code easier to read/maintain/add new features in the future.
Again, I know it was specified to avoid using switch, but it does look like the most simple solution here.
I think this will give some building blocks to go further, as others have mentioned use mapping to resolve the functions you need.
const data = {
shipping: 'Standard',
payment_type: 'VISA',
}
const handleVisaPayment = () => console.log('handling visa payment')
const handleStandardShipping = () => console.log('handling standard shipping')
const orderHandlingMap = {
'Standard': handleStandardShipping,
'VISA': handleVisaPayment,
}
const handleOrder = data =>
Object.values(data)
.filter(element => orderHandlingMap[element])
.map(handlerName => orderHandlingMap[handlerName]())
handleOrder(data)