pass argument from terminal via node to npm scripts - javascript

I want to do semi automation to create user for my api, so I have this script
//getUserInfo.js
const argv = require('yargs-parser')(process.argv.slice(2))
module.exports = (async () => {
let [ userId ] = argv._ //parsing...
if(!userId){
console.log('userId is not defined')
return
}
userId = userId.split('=')[1] ////parsing, again :(
console.log(userId) //123
//proceed with automation steps
...
...
})()
The script is working, so in my package.json I have this
"scripts": {
"admin:getUserInfo": "node server/scripts/getUserInfo.js"
}
All I need to do is to run npm run admin:getUserInfo userId=123 and I can get 123 in my terminal.
But the problem is I have to do so many step just to get the userId value.

Related

How to communicate with external CLIs with nodejs

When interacting with clis, for example, taking npm init, we can run the command and get the output by the following code
const { exec } = require('child_process');
exec('npm init', (err, stdout, stderr) => {
if (err) {
console.error(err)
} else {
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
}
});
But we cannot pass the project name, version name etc.. How to achieve this. Pls answer with the example of npm init command
Thanks in advance :)
Use the stdin channel each process provides. For that, use the node child_process.spawn method instead:
const { spawn } = require('child_process');
const npm = spawn('npm', ["init"]);
npm.stdout.pipe(process.stdout);
npm.stderr.pipe(process.stderr);
npm.on("exit", () => {
console.log("npm exited");
process.exit();
});
const answers = [
"my-awesome-cli", // package name
"0.0.1", // version number
"desciprtion", // description
"index.js", // entry point
"", // test command
"", // git reposiroty
"", // keywords
"Marc Stirner", // author
"MIT" // license
];
setInterval(() => {
if (answers.length > 0) {
// get first item from array
let answer = answers.shift();
// print value we pass to npm
console.log("Write to npm child:", answer);
// write chunk to stdin
npm.stdin.write(`${answer}\r\n`);
} else {
//npm.stdin.end();
console.log("Hit final enter")
npm.stdin.write(`\r\n`);
}
}, 800);
My example spwans the npm command, use the stdin channel to write the answer to the process, and pipe the stdout&stderr output from the npm command to the node.js process.
You can do this with exec too, since it returns as well a child_process object.
Read more on the node.js docs, they are very well documented.

Getting empty string from fs.readFile inside chokidar.watch(path_file).on('change', ...)

I have the following very simple Node project:
https://github.com/tlg-265/chokidar-issue
$ git clone https://github.com/tlg-265/chokidar-issue
$ cd chokidar-issue
$ npm i
$ npm run watch-changes
which basically takes care of detecting changes on file:
/profiles/bill-gates.json
and do an action just after that.
In order to do that I have the following file:
/profile-watcher.js
const fs = require('fs-extra');
const colors = require('colors/safe');
const chokidar = require('chokidar');
const path_file = `profiles/bill-gates.json`;
console.log(`Current Profile: ${colors.red.bgBrightYellow(path_file)}`);
let profile_before = {};
chokidar.watch(path_file).on('change', async (path) => {
console.log();
console.log(`${colors.blue.bgYellow(`->`)} Profile changed: ${path}`);
fs.readFile(path, (err, profile_json) => {
console.log(`->${profile_json}<-`);
let profile = JSON.parse(profile_json);
if (JSON.stringify(profile) != JSON.stringify(profile_before)) {
console.log('The profile has changed.');
profile_before = profile;
}
});
});
when I run the project with:
$ npm run watch-changes
and do the modifications below on file: /profiles/bill-gates.json
modification 1: Bill Gates -> Bill Gates ABC
modification 2: Bill Gates ABC -> Bill Gates ABC DEF
it works fine, outputting the content of this file to the console.
But when I do the next modification:
modification 3: Bill Gates ABC -> Bill Gates ABC DEF GHI
Then I get the following error:
-> Profile changed: profiles\bill-gates.json
-><-
undefined:1
SyntaxError: Unexpected end of JSON input
at JSON.parse (<anonymous>)
at fs.readFile (\chokidar-issue\profile-watcher.js:17:24)
at \chokidar-issue\node_modules\graceful-fs\graceful-fs.js:115:16
at FSReqWrap.readFileAfterClose [as oncomplete] (internal/fs/read_file_context.js:53:3)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! chokidar-issue#1.0.0 watch-changes: `node profile-watcher.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the chokidar-issue#1.0.0 watch-changes script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! C:\Roaming\npm-cache\_logs\2020-02-28T23_44_01_038Z-debug.log
/profiles/bill-gates.json (Flags: UTF-8 / CRLF)
{
"name": "Bill Gates",
"email": "bill.gates#microsoft.com",
"password": "windows",
"country": "USA"
}
By the way, if I change from CRLF to LF normally I can do few modifications more before it crashes.
I'm under the impression that for somre reason the file: /profiles/bill-gates.json gets locked at some point and when Node tries to read it it returns an empty string because it is locked.
Any idea on how to make this work without crashing after few tries?
Thanks!
I had the same problem as you.
There is an option in "chokidar" where you can awaitWriteFinish. It's time-based and checks if the size of the file is changing. If not, then it will call the callback.
const watcher = chokidar.watch(configPathString, {
persistent: true,
awaitWriteFinish: {
stabilityThreshold: 500
}
});
watcher.on('change', (path, stats) => {
fs.readFile(configPathString,(err, data)=>{
if (err) throw err;
//console.log('data',data);
let receivedData = JSON.parse(data);
//Do whatever you like
})
});
It may be a race condition. Make your JSON.parse safe like this:
const path = require('path')
chokidar.watch(path_file).on('change', async (path) => {
fs.readFile(path, 'utf8', (err, profile_json) => {
if (!profile_json) {
console.log(`${path} is an empty file!`)
return
}
const profile = JSON.parse(profile_json);
if (JSON.stringify(profile) != JSON.stringify(profile_before)) {
console.log('The profile has changed.');
profile_before = profile;
}
});
});
I could make it work by adding some recovery fallback:
const fs = require('fs-extra');
const colors = require('colors/safe');
const chokidar = require('chokidar');
const sleep = require('sleep');
const path_file = `profiles/bill-gates.json`;
console.log(`Current Profile: ${colors.red.bgBrightYellow(path_file)}`);
let profile_before = fs.readFileSync(path_file).toString();
chokidar.watch(path_file).on('change', async (path_changed) => {
let profile = fs.readFileSync(path_changed).toString();
if (IsValidJson(profile)) {
if (profile != profile_before) {
console.log();
console.log(`Profile changed: ${colors.red.bgBrightYellow(path_changed)}`);
process_profile(profile);
profile_before = profile;
}
}
else {
sleep.msleep(100); // this is necessary
}
});
function process_profile(profile_json) {
const profile = JSON.parse(profile_json);
console.log(`${profile_json}`);
console.log(profile.name);
}
function IsValidJson(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
It seems that when you save a file (at least on Windows), sometimes there is a time in between (very very short time) that the file gets clear and few milliseconds later it gets the actual content. On both cases the on-change event gets fired. So, we just need to verify whether the content of the file is JSON or not. In that case I just need to ignore it and wait for the next on-change event.

How to fix failing NextJS export - working fine locally but upon export it's failing, dismally :(

I'm working on a video/music streaming application in Next/React.JS, fetching data from a Wordpress API/Backend/headless CMS. It's working great on localhost (though it's real barebones functionality at the moment) - however, when I attempt to export it to create a static front end the export is failing repeatedly with the (ridiculously common and usually straightforward) 'cannot read property of undefined' error.
I've spent the last 12 hours debugging rigorously and scanning here/GH etc, using all combinations of then()/catch() await etc under the sun, but I just can't get it to work and can't for the life of me figure out why. I'm aware 'title' is undefined because the data hasn't been fetched at the time of exporting but how to get this past'next export'?
Here's my getInitialProps from single.js - where the problem seems to be, getting no other relevant errors in the console - I'll post the terminal message upon attempting to export below. This is where I've come back to after dozens of versions - it's been a long day, so there may be a silly mistake or two, but this is functioning locally, without any errors.
static async getInitialProps(context) {
const slug = context.query.slug;
let post = {};
// Make request for posts.
try {
const response = await axios.get(
`http://timeline-music-30.local/wp-json/wp/v2/posts?slug=${slug}`
);
post = response.data[0];
} catch (err) {
console.error(err);
}
return { post };
// Return our only item in array from response to posts object in props.
console.log("post:", post);
}
I expect the application to export to a static site successfully, but it fails with the following terminal message:
copying "static build" directory
launching 3 threads with concurrency of 10 per thread
[==--] 2/4 50% 118/s 0.0s TypeError: Cannot read property 'title' of undefined
at _default.render (C:\Users\Terry\Documents\github\projects\timeline-music-3.0\nextjs\.next\server\static\YyI9s0TjENSVhc1SFZUcV\pages\single.js:158:35)
Any ideas/help would be greatly appreciated.
Thanks
Terry
First, you can't console.log after return.
Second. Use isomorphic-fetch and this construction, i think that in your case help this:
static async getInitialProps(context) {
const slug = context.query.slug;
// Make request for posts.
const resPost = await fetch('http://timeline-music-30.local/wp-json/wp/v2/posts?slug=${slug}');
const dataPost = await resPost.json();
console.log("post:", dataPost.data[0]);
return { post: dataPost.data[0] };
}
In component use {this.props.post}.
If this not helped, look at my case, it's working in local and production:
In my similar case, I solved about the same problem:
I solved this problem on the site http://computers.remolet.ru/. The task was to produce different content depending on the domain, so the pages request content from the API via fetch. Here is how I solved this problem:
Add to module top:
import getConfig from 'next/config';
const nextConfig = getConfig();
// npm i isomorphic-fetch need
import 'isomorphic-fetch';
Fetching on page:
static async getInitialProps ({ ctx }) {
var host = '';
if (nextConfig && nextConfig.publicRuntimeConfig && nextConfig.publicRuntimeConfig.HOST) {
// server side
host = nextConfig.publicRuntimeConfig.HOST;
} else if (ctx && ctx.req && ctx.req.headers) {
// front side
host = 'http://' + ctx.req.headers.host;
} else {
// front side
host = 'http://' + window.location.host;
}
const resPricelist = await fetch(host + '/api/pricelist');
const dataPricelist = await resPricelist.json();
const resNewPricelist = await fetch(host + '/api/newpricelist');
const dataNewPricelist = await resNewPricelist.json();
const resContent = await fetch(host + '/api/content');
const dataContent = await resContent.json();
return {
pricelistData: dataPricelist,
newPricelistData: dataNewPricelist,
contentData: dataContent
};
}
Using in component:
<Header as="h1" align="center">
{this.props.contentData.h1}
</Header>
In next.config.js:
module.exports = withCSS(withSass({
cssModules: true,
serverRuntimeConfig: {
PORT: process.env.PORT, // eslint-disable-line no-process-env
HOST: process.env.HOST, // eslint-disable-line no-process-env
CONTENT: process.env.CONTENT // eslint-disable-line no-process-env
},
publicRuntimeConfig: {
PORT: process.env.PORT, // eslint-disable-line no-process-env
HOST: process.env.HOST, // eslint-disable-line no-process-env
CONTENT: process.env.CONTENT // eslint-disable-line no-process-env
}
}));
Starting Node with environment:
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "HOST='http://localhost:8000' PORT='8000' CONTENT='./db/computers.json' PRICELIST='./db/pricelist/computers.json' PRICELIST2='./db/pricelist/newComputers.json' node server.js",
"build": "next build",
"start": "next start"
},
This working on localhost and server.
Just use isomorphic-fetch and if you fetch from absolute url all you need is construction:
const resPricelist = await fetch(host + '/api/pricelist');
const dataPricelist = await resPricelist.json();
return {
data: dataPricelist
}
This is the result of approximately 20 hours of trying and reading the Next.js forums.
Hope i help you :)
P.S. Don't forget what you can use getInitialProps ({ ctx }) ONLY on page component, not in child components!

Error with Firebase deploy function to send push notifications

I'm developing an iOS app and now I'm stuck with Firebase deploy functions. I'm trying to send push notifications and I prepared the codes like below.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.pushNotifications = functions.database.ref('/messages/{messageId}')
.onCreate(event => {
const data = event.data;
const fromId = data.fromId;
const toId = data.toId;
const message = data.message;
console.log(fromId + ' sent a message to' + toId);
return admin.database().ref('/users/' + fromId).once('value', snapshot => {
var user = snapshot.val();
var payload = {
notification: {
title: user.username,
body: message
}
}
admin.messaging().sendToDevice(user.fcmToken, payload)
.then(function(response) {
// See the MessagingDevicesResponse reference documentation for
// the contents of response.
console.log("Successfully sent message:", response);
})
.catch(function(error) {
console.log("Error sending message:", error);
});
})
Database structure:
messages - messageId -fromId
└toId
└Message
└ messageId -fromId
└toId
└Message
.
.
.
And this is the error message.
37:1 error Parsing error: Unexpected token
✖ 1 problem (1 error, 0 warnings)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! functions# lint: `eslint .`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the functions# lint script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/...
Error: functions predeploy error: Command terminated with non-zero exit code1
Also in the log, I get errors like:
TypeError: Cannot read property 'fromId' of undefined
Is the error occurring because I'm not fetching fcmToken right?
I've never coded with JavaScirpt. I would appreciate any suggestion!
Change this:
exports.pushNotifications = functions.database.ref('/messages/{messageId}')
.onCreate(event => {
const data = event.data;
const fromId = data.fromId;
const toId = data.toId;
const message = data.message;
into this:
exports.pushNotifications = functions.database.ref('/messages/{messageId}')
.onCreate((snap,context) => {
const data = snap.val();
const fromId = data.fromId;
const toId = data.toId;
const message = data.message;
});
Check here for more info:
https://firebase.google.com/docs/functions/beta-v1-diff
You are most likely running v1 of firebase functions, which is the latest version which brought quite a few changes to the api. You can read more about the changes here. Specifically, you want to change your event => parameter to (snap, context) =>
exports.dbWrite = functions.database.ref('/path').onCreate((snap, context) => {
const data = snap.val();
const { fromId, toId } = data;
...
});

Async Generator works on babel-node but fails w/ babel-register or build

Using babel-node I was able to run the following code
function timeout(ms = 100) {
return new Promise(resolve => {
let id = setTimeout(() => {
clearTimeout(id)
resolve(ms)
}, ms)
})
}
async function* worker(limit = 10) {
async function fetch() {
return await timeout(Math.random() * 1000)
}
let low = 0;
while (low++ < limit) yield await fetch()
}
async function run() {
const gen = worker(5)
const results = [];
for await (const res of gen) {
console.log('working')
results.push(res)
}
return 'done'
}
run().then(res => console.log(res)).catch(err => console.error(err))
Doesn't work here, but works on the online Babel REPL
As well as when I run it through babel-node like:
babel-node src/script.js
It however fails when I build and run it like so:
babel src/script.js --out-file dist/script.js
node dist/script.js
and gives me
TypeError: iterable[Symbol.iterator] is not a function
Using babel-register also fails w/ the same error:
node -r babel-register -r dotenv/config src/script.js
My current .babelrc looks like
{
"plugins": ["transform-strict-mode", "transform-async-generator-functions"],
"presets": ["es2015-node6", "stage-2"]
}
using es2015 instead of es2015-node6 yielded no benefit
When I looked at the default plugins and presets used for babel-node here , and looks like they're empty
What am I missing?
babel-node (and the online REPL), in addition to handling runtime transpiling, also requires babel-polyfill. You should npm i -S babel-polyfill and then import 'babel-polyfill'; at your program's entrypoint (or in your example, add -r babel-polyfill to your node args).

Categories

Resources