Is it possible to access npm config settings from a node package's `bin` command? - node.js

I want to add some default configuration into a shell tool that the user can override using npm config. It looks like this is possible but this only works for npm scripts not my package's binary as specified by "bin" below.
Package.json
{
"name": "tmp",
"scripts": {
"test": "node index.js"
},
"bin" : {
"tmp" : "index.js"
},
"config": {
"foo" : 123
}
}
index.js
#!/usr/bin/env node
console.log(process.env.npm_package_config_foo);
My attempts
calling binary
setup for this is running npm link in the package to make a global link to that package
$ tmp
undefined
passing script to node
$ node ./index.js
undefined
calling as npm script ⫸ only this one works
$ npm test
> tmp# test /private/tmp
> node index.js
123
Question
How can I access these config values in a way that's overridable by the user like npm config allows? (To be clear I want to do it the npm config way; I know there are other ways to skin this particular cat.)

You can use a package called npm-conf with documentation at https://github.com/kevva/npm-conf.
First you need to install it:
npm install --save npm-conf
Usage is as follows:
const npmConf = require('npm-conf');
const conf = npmConf();
conf.get('prefix')
//=> //=> /Users/unicorn/.npm-packages
conf.get('registry')
//=> https://registry.npmjs.org/
If you want to get a list of all available NPM configuration options:
npm config list -l

Related

Node v15.5.0 doesn't read command line flags [duplicate]

The scripts portion of my package.json currently looks like this:
"scripts": {
"start": "node ./script.js server"
}
...which means I can run npm start to start the server. So far so good.
However, I would like to be able to run something like npm start 8080 and have the argument(s) passed to script.js (e.g. npm start 8080 => node ./script.js server 8080). Is this possible?
npm 2 and newer
It's possible to pass args to npm run since npm 2 (2014). The syntax is as follows:
npm run <command> [-- <args>]
Note the -- separator, used to separate the params passed to npm command itself, and the params passed to your script.
With the example package.json:
"scripts": {
"grunt": "grunt",
"server": "node server.js"
}
here's how to pass the params to those scripts:
npm run grunt -- task:target // invokes `grunt task:target`
npm run server -- --port=1337 // invokes `node server.js --port=1337`
Note: If your param does not start with - or --, then having an explicit -- separator is not needed; but it's better to do it anyway for clarity.
npm run grunt task:target // invokes `grunt task:target`
Note below the difference in behavior (test.js has console.log(process.argv)): the params which start with - or -- are passed to npm and not to the script, and are silently swallowed there.
$ npm run test foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', 'foobar']
$ npm run test -foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js']
$ npm run test --foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js']
$ npm run test -- foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', 'foobar']
$ npm run test -- -foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', '-foobar']
$ npm run test -- --foobar
['C:\\Program Files\\nodejs\\node.exe', 'C:\\git\\myrepo\\test.js', '--foobar']
The difference is clearer when you use a param actually used by npm:
$ npm test --help // this is disguised `npm --help test`
npm test [-- <args>]
aliases: tst, t
To get the parameter value, see this question. For reading named parameters, it's probably best to use a parsing library like yargs or minimist; nodejs exposes process.argv globally, containing command line parameter values, but this is a low-level API (whitespace-separated array of strings, as provided by the operating system to the node executable).
You asked to be able to run something like npm start 8080. This is possible without needing to modify script.js or configuration files as follows.
For example, in your "scripts" JSON value, include--
"start": "node ./script.js server $PORT"
And then from the command-line:
$ PORT=8080 npm start
I have confirmed that this works using bash and npm 1.4.23. Note that this work-around does not require GitHub npm issue #3494 to be resolved.
You could also do that:
In package.json:
"scripts": {
"cool": "./cool.js"
}
In cool.js:
console.log({ myVar: process.env.npm_config_myVar });
In CLI:
npm --myVar=something run-script cool
Should output:
{ myVar: 'something' }
Update: Using npm 3.10.3, it appears that it lowercases the process.env.npm_config_ variables? I'm also using better-npm-run, so I'm not sure if this is vanilla default behavior or not, but this answer is working. Instead of process.env.npm_config_myVar, try process.env.npm_config_myvar
jakub.g's answer is correct, however an example using grunt seems a bit complex.
So my simpler answer:
- Sending a command line argument to an npm script
Syntax for sending command line arguments to an npm script:
npm run [command] [-- <args>]
Imagine we have an npm start task in our package.json to kick off webpack dev server:
"scripts": {
"start": "webpack-dev-server --port 5000"
},
We run this from the command line with npm start
Now if we want to pass in a port to the npm script:
"scripts": {
"start": "webpack-dev-server --port process.env.port || 8080"
},
running this and passing the port e.g. 5000 via command line would be as follows:
npm start --port:5000
- Using package.json config:
As mentioned by jakub.g, you can alternatively set params in the config of your package.json
"config": {
"myPort": "5000"
}
"scripts": {
"start": "webpack-dev-server --port process.env.npm_package_config_myPort || 8080"
},
npm start will use the port specified in your config, or alternatively you can override it
npm config set myPackage:myPort 3000
- Setting a param in your npm script
An example of reading a variable set in your npm script. In this example NODE_ENV
"scripts": {
"start:prod": "NODE_ENV=prod node server.js",
"start:dev": "NODE_ENV=dev node server.js"
},
read NODE_ENV in server.js either prod or dev
var env = process.env.NODE_ENV || 'prod'
if(env === 'dev'){
var app = require("./serverDev.js");
} else {
var app = require("./serverProd.js");
}
As of npm 2.x, you can pass args into run-scripts by separating with --
Terminal
npm run-script start -- --foo=3
Package.json
"start": "node ./index.js"
Index.js
console.log('process.argv', process.argv);
I had been using this one-liner in the past, and after a bit of time away from Node.js had to try and rediscover it recently. Similar to the solution mentioned by #francoisrv, it utilizes the npm_config_* variables.
Create the following minimal package.json file:
{
"name": "argument",
"version": "1.0.0",
"scripts": {
"argument": "echo \"The value of --foo is '${npm_config_foo}'\""
}
}
Run the following command:
npm run argument --foo=bar
Observe the following output:
The value of --foo is 'bar'
All of this is nicely documented in the npm official documentation:
https://docs.npmjs.com/using-npm/config
Note: The Environment Variables heading explains that variables inside scripts do behave differently to what is defined in the documentation. This is true when it comes to case sensitivity, as well whether the argument is defined with a space or equals sign.
Note: If you are using an argument with hyphens, these will be replaced with underscores in the corresponding environment variable. For example, npm run example --foo-bar=baz would correspond to ${npm_config_foo_bar}.
Note: For non-WSL Windows users, see #Doctor Blue's comments below... TL;DR replace ${npm_config_foo} with %npm_config_foo%.
Use process.argv in your code then just provide a trailing $* to your scripts value entry.
As an example try it with a simple script which just logs the provided arguments to standard out echoargs.js:
console.log('arguments: ' + process.argv.slice(2));
package.json:
"scripts": {
"start": "node echoargs.js $*"
}
Examples:
> npm start 1 2 3
arguments: 1,2,3
process.argv[0] is the executable (node), process.argv[1] is your script.
Tested with npm v5.3.0 and node v8.4.0
Most of the answers above cover just passing the arguments into your NodeJS script, called by npm. My solution is for general use.
Just wrap the npm script with a shell interpreter (e.g. sh) call and pass the arguments as usual. The only exception is that the first argument number is 0.
For example, you want to add the npm script someprogram --env=<argument_1>, where someprogram just prints the value of the env argument:
package.json
"scripts": {
"command": "sh -c 'someprogram --env=$0'"
}
When you run it:
% npm run -s command my-environment
my-environment
If you want to pass arguments to the middle of an npm script, as opposed to just having them appended to the end, then inline environment variables seem to work nicely:
"scripts": {
"dev": "BABEL_ARGS=-w npm run build && cd lib/server && nodemon index.js",
"start": "npm run build && node lib/server/index.js",
"build": "mkdir -p lib && babel $BABEL_ARGS -s inline --stage 0 src -d lib",
},
Here, npm run dev passes the -w watch flag to babel, but npm run start just runs a regular build once.
For PowerShell users on Windows
The accepted answer did not work for me with npm 6.14. Neither adding no -- nor including it once does work. However, putting -- twice or putting "--" once before the arguments does the trick. Example:
npm run <my_script> -- -- <my arguments like --this>
Suspected reason
Like in bash, -- instructs PowerShell to treat all following arguments as literal strings, and not options (E.g see this answer). The issues seems to be that the command is interpreted one time more than expected, loosing the '--'. For instance, by doing
npm run <my_script> -- --option value
npm will run
<my_script> value
However, doing
npm run <my_script> "--" --option value
results in
<my_script> "--option" "value"
which works fine.
This doesn't really answer your question but you could always use environment variables instead:
"scripts": {
"start": "PORT=3000 node server.js"
}
Then in your server.js file:
var port = process.env.PORT || 3000;
I've found this question while I was trying to solve my issue with running sequelize seed:generate cli command:
node_modules/.bin/sequelize seed:generate --name=user
Let me get to the point. I wanted to have a short script command in my package.json file and to provide --name argument at the same time
The answer came after some experiments. Here is my command in package.json
"scripts: {
"seed:generate":"NODE_ENV=development node_modules/.bin/sequelize seed:generate"
}
... and here is an example of running it in terminal to generate a seed file for a user
> yarn seed:generate --name=user
> npm run seed:generate -- --name=user
FYI
yarn -v
1.6.0
npm -v
5.6.0
Note: This approach modifies your package.json on the fly, use it if you have no alternative.
I had to pass command line arguments to my scripts which were something like:
"scripts": {
"start": "npm run build && npm run watch",
"watch": "concurrently \"npm run watch-ts\" \"npm run watch-node\"",
...
}
So, this means I start my app with npm run start.
Now if I want to pass some arguments, I would start with maybe:
npm run start -- --config=someConfig
What this does is: npm run build && npm run watch -- --config=someConfig. Problem with this is, it always appends the arguments to the end of the script. This means all the chained scripts don't get these arguments(Args maybe or may not be required by all, but that's a different story.). Further when the linked scripts are called then those scripts won't get the passed arguments. i.e. The watch script won't get the passed arguments.
The production usage of my app is as an .exe, so passing the arguments in the exe works fine but if want to do this during development, it gets problamatic.
I couldn't find any proper way to achieve this, so this is what I have tried.
I have created a javascript file: start-script.js at the parent level of the application, I have a "default.package.json" and instead of maintaining "package.json", I maintain "default.package.json". The purpose of start-script.json is to read default.package.json, extract the scripts and look for npm run scriptname then append the passed arguments to these scripts. After this, it will create a new package.json and copy the data from default.package.json with modified scripts and then call npm run start.
const fs = require('fs');
const { spawn } = require('child_process');
// open default.package.json
const defaultPackage = fs.readFileSync('./default.package.json');
try {
const packageOb = JSON.parse(defaultPackage);
// loop over the scripts present in this object, edit them with flags
if ('scripts' in packageOb && process.argv.length > 2) {
const passedFlags = ` -- ${process.argv.slice(2).join(' ')}`;
// assuming the script names have words, : or -, modify the regex if required.
const regexPattern = /(npm run [\w:-]*)/g;
const scriptsWithFlags = Object.entries(packageOb.scripts).reduce((acc, [key, value]) => {
const patternMatches = value.match(regexPattern);
// loop over all the matched strings and attach the desired flags.
if (patternMatches) {
for (let eachMatchedPattern of patternMatches) {
const startIndex = value.indexOf(eachMatchedPattern);
const endIndex = startIndex + eachMatchedPattern.length;
// save the string which doen't fall in this matched pattern range.
value = value.slice(0, startIndex) + eachMatchedPattern + passedFlags + value.slice(endIndex);
}
}
acc[key] = value;
return acc;
}, {});
packageOb.scripts = scriptsWithFlags;
}
const modifiedJSON = JSON.stringify(packageOb, null, 4);
fs.writeFileSync('./package.json', modifiedJSON);
// now run your npm start script
let cmd = 'npm';
// check if this works in your OS
if (process.platform === 'win32') {
cmd = 'npm.cmd'; // https://github.com/nodejs/node/issues/3675
}
spawn(cmd, ['run', 'start'], { stdio: 'inherit' });
} catch(e) {
console.log('Error while parsing default.package.json', e);
}
Now, instead of doing npm run start, I do node start-script.js --c=somethis --r=somethingElse
The initial run looks fine, but haven't tested thoroughly. Use it, if you like for you app development.
I find it's possible to just pass variables exactly as you would to Node.js:
// index.js
console.log(process.env.TEST_ENV_VAR)
// package.json
...
"scripts": { "start": "node index.js" },
...
TEST_ENV_VAR=hello npm start
Prints out "hello"
From what I see, people use package.json scripts when they would like to run script in simpler way. For example, to use nodemon that installed in local node_modules, we can't call nodemon directly from the cli, but we can call it by using ./node_modules/nodemon/nodemon.js. So, to simplify this long typing, we can put this...
...
scripts: {
'start': 'nodemon app.js'
}
...
... then call npm start to use 'nodemon' which has app.js as the first argument.
What I'm trying to say, if you just want to start your server with the node command, I don't think you need to use scripts. Typing npm start or node app.js has the same effort.
But if you do want to use nodemon, and want to pass a dynamic argument, don't use script either. Try to use symlink instead.
For example using migration with sequelize. I create a symlink...
ln -s node_modules/sequelize/bin/sequelize sequelize
... And I can pass any arguement when I call it ...
./sequlize -h /* show help */
./sequelize -m /* upgrade migration */
./sequelize -m -u /* downgrade migration */
etc...
At this point, using symlink is the best way I could figure out, but I don't really think it's the best practice.
I also hope for your opinion to my answer.
Separate your arguments using -- from the script and add all the required arguments, we can later access them by index.
npm run start -- myemail#gmail.com 100
You can get params in node using
const params = process.argv.slice(2);
console.log(params);
Output
['myemail#gmail.com', '100']
I know there is an approved answer already, but I kinda like this JSON approach.
npm start '{"PROJECT_NAME_STR":"my amazing stuff", "CRAZY_ARR":[0,7,"hungry"], "MAGICAL_NUMBER_INT": 42, "THING_BOO":true}';
Usually I have like 1 var I need, such as a project name, so I find this quick n' simple.
Also I often have something like this in my package.json
"scripts": {
"start": "NODE_ENV=development node local.js"
}
And being greedy I want "all of it", NODE_ENV and the CMD line arg stuff.
You simply access these things like so in your file (in my case local.js)
console.log(process.env.NODE_ENV, starter_obj.CRAZY_ARR, starter_obj.PROJECT_NAME_STR, starter_obj.MAGICAL_NUMBER_INT, starter_obj.THING_BOO);
You just need to have this bit above it (I'm running v10.16.0 btw)
var starter_obj = JSON.parse(JSON.parse(process.env.npm_config_argv).remain[0]);
Anyhoo, question already answered. Thought I'd share, as I use this method a lot.
I settled for something like this, look at the test-watch script:
"scripts": {
"dev": "tsc-watch --onSuccess \"node ./dist/server.js\"",
"test": "tsc && cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest",
"test-watch": "cross-env NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 tsc-watch --onSuccess",
},
You invoke the test-watch script like this:
// Run all tests with odata in their name
npm run test-watch "jest odata"
npm run script_target -- < argument > Basically this is the way of passing the command line arguments but it will work only in case of when script have only one command running like I am running a command i.e. npm run start -- 4200
"script":{
"start" : "ng serve --port="
}
This will run for passing command line parameters but what if we run more then one command together like npm run build c:/workspace/file
"script":{
"build" : "copy c:/file <arg> && ng build"
}
but it will interpreter like this while running copy c:/file && ng build c:/work space/file
and we are expected something like this
copy c:/file c:/work space/file && ng build
Note :- so command line parameter only work ad expected in case of only one command in a script.
I read some answers above in which some of them are writing that you can access the command line parameter using $ symbol but this will not gonna work
Try cross-env NPM package.
Easy to use. Easy to install. Cross all platform.
Example:
set arguments for command
// package.json
"scripts": {
“test”: “node test.js”,
“test-with-env-arg”: “cross-env YourEnvVarName=strValue yarn test,
}
get arguments from process.env
// test.js
const getCommandLineArg = Boolean(process.env.YourEnvVarName === 'true') // Attention: value of process.env.* is String type, not number || boolean
i had the same issue when i need to deploy to different environments
here is the package.json pre and post the updates.
scripts:
{"deploy-sit": "sls deploy --config resources-sit.yml",
"deploy-uat": "sls deploy --config resources-uat.yml",
"deploy-dev": "sls deploy --config resources-dev.yml"}
but here is the correct method to adopt the environment variables rather than repeating ourselves
scripts:{"deploy-env": "sls deploy --config resources-$ENV_VAR.yml"}
finally you can deploy by running
ENV_VAR=dev npm run deploy-env

How to create a 'common package' to share common logic in convector packages?

I want to create a common package to share common login in convector chain code...this is useful has the project grows and we want to share common blocks of code, utils and other useful code to don't dry the project, or hard-code strings
but how can we do it if convector currently don't support it?
Currently convector can't use common vanilla packages (yet), only controllers,
to work we need to hack some stuff, like create a package that implement a controller, like a fake controller. and follow some steps
thanks to Diego and Walter of Covalent and a few hours
this post belongs to a github project that have a nest.js server, but currently is not created, when I have the link I update this post
first we start to create a lerna package for typescript, by hand
Create lerna common package #convector-rest-sample/common
packages/common/tsconfig.json
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "."
},
"include": [
"./src/**/*"
]
}
packages/common/package.json
{
"name": "#convector-rest-sample/common",
"version": "0.1.0",
"main": "dist/src/index",
"types": "dist/src/index",
"files": [
"dist"
],
"scripts": {
"build": "npm run clean && tsc",
"clean": "rm -rf ./dist",
"prepublishOnly": "npm run build"
},
"devDependencies": {
"typescript": "3.4.3"
}
}
Note: the paths "main": "dist/src/index" and "types": "dist/src/index" are important and must point to location of the index file, if we use a wrong path, we get errors using the package
create the index to export package files
packages/common/src/index.ts
export * from './constants';
packages/common/src/constants.ts
// convector model
const CONVECTOR_MODEL_PATH_PREFIX: string = 'io.worldsibu.examples';
const CONVECTOR_MODEL_PATH_PARTICIPANT: string = `${CONVECTOR_MODEL_PATH_PREFIX}.participant`;
const CONVECTOR_MODEL_PATH_PERSON: string = `${CONVECTOR_MODEL_PATH_PREFIX}.person`;
const CONVECTOR_MODEL_PATH_ATTRIBUTE: string = `${CONVECTOR_MODEL_PATH_PREFIX}.attribute`;
const CONVECTOR_MODEL_PATH_X509IDENTITY: string = `${CONVECTOR_MODEL_PATH_PREFIX}.x509identity`;
export const appConstants = {
CONVECTOR_MODEL_PATH_PARTICIPANT,
CONVECTOR_MODEL_PATH_PERSON,
CONVECTOR_MODEL_PATH_ATTRIBUTE,
CONVECTOR_MODEL_PATH_X509IDENTITY,
};
now add the common package to all packages in monorepo
# add to all packages (without scope)
$ npx lerna add #convector-rest-sample/common#0.1.0
# to prevent some problems always use same version has in local package
# clean and bootstrap
$ npx lerna clean -y && npx lerna bootstrap
optional can use --scope to add only to desired packages
# add to all packages (with scope)
$ npx lerna add #convector-rest-sample/common#0.1.0 --scope server --no-bootstrap
$ npx lerna add #convector-rest-sample/common#0.1.0 --scope participant-cc --no-bootstrap
$ npx lerna add #convector-rest-sample/common#0.1.0 --scope person-cc --no-bootstrap
# clean and bootstrap
$ npx lerna clean -y && npx lerna bootstrap --hoist
now test #convector-rest-sample/common in server, add this lines to top of packages/server/src/app.ts
to confirm that everything is working has expected
Note: if don't have a server, skip this step right to Use common package inside ChainCode section
import { appConstants as c } from '#convector-rest-sample/common';
debugger;
Logger.log(JSON.stringify(c, undefined, 2));
now launch server with debugger, and inspect c object or view log result
$ npx lerna run start:debug --scope server --stream
if outputs appConstants we are ready to go, and common package works has expected
Use common package inside ChainCode
To use common package inside chaincode, is very tricky, and I lost a few hours to get it work, thanks to Walter and Diego from WorldSibu I get it.
The problem is that currently, in convector there is no easy way to use packages, that are not controllers, for this to work we must create a fake controller in #convector-rest-sample/common to put it to work
first install required controller dependency in our #convector-rest-sample/common, this is required ut use { Controller, Invokable }
# install dependency
$ npx lerna add #worldsibu/convector-core --scope #convector-rest-sample/common
next we must create a fake controller in packages/common/src/common.controller.ts
import { Controller, Invokable } from '#worldsibu/convector-core';
#Controller('common')
export class CommonController {
#Invokable()
public async greeting() {
return 'Hello from CommonController';
}
}
don't forget to add export * from './common.controller'; to packages/common/src/index.ts to export controller
export * from './constants';
export * from './common.controller';
after that we must change chaincode.config.json to add the fake controller, this is a hell of a hack, we use a fake controller to force the #convector-rest-sample/common to be copied inside chaincode-person dir, without this, the #convector-rest-sample/common is not copied and we have a broken chain code when we try deploy it with cc:start or cc:upgrade it always show the annoying error npm ERR! 404 Not Found: #convector-rest-sample/common#0.1.0
first change chaincode.config.json
Tip: if don't have it in project, copy org1.person.config.json to chaincode.config.json and move on....
in my case I have only the legacy files org1.participant.config.json, org1.person.config.json, org2.participant.config.json and org2.person.config.json
this files can be deleted (Diego tip)
ok let's change chaincode.config.json and add another controller above person-cc
"controllers": [
{
"name": "participant-cc",
"version": "file:./packages/participant-cc",
"controller": "ParticipantController"
},
{
"name": "person-cc",
"version": "file:./packages/person-cc",
"controller": "PersonController"
},
// BO : ADD THIS
{
"name": "#convector-rest-sample/common",
"version": "file:./packages/common",
"controller": "CommonController"
}
// EO : ADD THIS
],
Note: this is another clever tricky part, the name is the package name, like the one we use in imports, version is the path location inside of our build chaincode-person
before build chaincode we must change our models to use the new common constants from #convector-rest-sample/common ex c.CONVECTOR_MODEL_PATH_X509IDENTITY, currently this common package only use simple constants, to keep it simple, the point is created common logic for all the packages, rest-server, front-end, packages-cc, cli-tools, etc
packages/participant-cc/src/participant.model.ts
import { appConstants as c } from '#convector-rest-sample/common';
...
export class x509Identities extends ConvectorModel<x509Identities>{
#ReadOnly()
public readonly type = c.CONVECTOR_MODEL_PATH_X509IDENTITY;
...
export class Participant extends ConvectorModel<Participant> {
#ReadOnly()
public readonly type = c.CONVECTOR_MODEL_PATH_PARTICIPANT;
...
packages/person-cc/src/person.model.ts
import { appConstants as c } from '#convector-rest-sample/common';
...
export class Attribute extends ConvectorModel<Attribute>{
#ReadOnly()
#Required()
public readonly type = c.CONVECTOR_MODEL_PATH_ATTRIBUTE;
...
export class Person extends ConvectorModel<Person> {
#ReadOnly()
#Required()
public readonly type = 'io.worldsibu.example.person';
...
now we can cc:package the chaincode chaincode-person, this will package the chaincode with our #convector-rest-sample/common inside it with packages person-cc and participant-cc using our #convector-rest-sample/common constants
# first remove chaincode (optional)
$ rm chaincode-person -r
# now manually invoke package command
$ npm run cc:package -- person org1
after package our chaincode is advised to check if common package is copied to chaincode-person folder to the right path
$ ls -la chaincode-person/packages
chaincode-person/packages/#convector-rest-sample
chaincode-person/packages/participant-cc
chaincode-person/packages/person-cc
another good practice is check if inside chaincode-person folder, in file chaincode-person/package.json, if our #convector-rest-sample/common was added to dependencies, in above json block we can see cc:package script add line "#convector-rest-sample/common": "file:./package/#convector-rest-sample/common", this is created based on our changes in chaincode.config.json remember, when we add the fake controller
"dependencies": {
"#theledger/fabric-chaincode-utils": "^4.0.1",
"#worldsibu/convector-core": "^1.3.3",
"#worldsibu/convector-storage-stub": "^1.3.3",
"reflect-metadata": "^0.1.12",
"tslib": "^1.9.0",
"participant-cc": "file:./packages/participant-cc",
"person-cc": "file:./packages/person-cc",
// BO: magic line here
"#convector-rest-sample/common": "file:./packages/#convector-rest-sample/common"
// EO: magic line here
},
done now we can deploy our chaincode with cc:start or cc:upgrade
Tip: if is first time use cc:start, if not use cc:package
to check that everything is fine from start, we restart our hyperledger stack, and start from the beginning, warning it destroy all data, if don't want to destroy data don't fire npm run env:restart and use cc:upgrade, more above
# this recreate environment and destroy all data
$ npm run env:restart
$ npm run cc:start -- person
# seed ledger
$ npm run seed
# create couchdb views
$ ./views/install.sh
# invoke person_create
$ npx hurl invoke person person_create "{ \"id\": \"1-100-103\", \"firstname\": \"Pete\", \"lastname\": \"Doe\", \"username\": \"pete\", \"password\": \"12345678\", \"email\": \"pete.doe#example.com\"}" -u admin
# invoke some stuff (wait for first invoke finish)
$ npx hurl invoke person person_getAll
done, everything is working has expected and we have a #convector-rest-sample/common package implemented.
if we check couchdb 1-100-103 person, we can check that is using type "type": "io.worldsibu.examples.person" that comes from our constants in our #convector-rest-sample/common, proving that it gets its value from #convector-rest-sample/common, believe me, if it won't wont find #convector-rest-sample/common it crash.....simple
for future changes in chaincode, upgrade it with above command
# upgrade chaincode
$ npm run cc:upgrade -- person 1.1
we are done........
Use scripts to copy other files to chaincode
another thing that I tried to hack before find the solution, is using npm scripts but it won't work because we need the modified chaincode-person/package.json with "#convector-rest-sample/common": "file:./packages/#convector-rest-sample/common" in the dependencies, but I try it......
leave it here, maybe can be useful for other kind of stuff, like copy other type of stuff
{
"scripts": {
...
"cc:package": "f() { npm run lerna:build; chaincode-manager --update --config ./$2.$1.config.json --output ./chaincode-$1 package; npm run copy:indexes -- $1; npm run copy:package:common -- $1; }; f",
...
"copy:package:common": "f () { mkdir -p ./chaincode-$1/node_modules/#convector-rest-sample/; cp -r ./packages/common/ ./chaincode-$1/node_modules/#convector-rest-sample/; }; f"
...
note for npm run copy:package:common -- $1; in "cc:package", and cp -r ./packages/common/ ./chaincode-$1/node_modules/#convector-rest-sample/; }; in "copy:package:common", it works, but won't modify chaincode-person/package.json with lines
"dependencies": {
"#theledger/fabric-chaincode-utils": "^4.0.1",
"#worldsibu/convector-core": "^1.3.3",
"#worldsibu/convector-storage-stub": "^1.3.3",
"reflect-metadata": "^0.1.12",
"tslib": "^1.9.0",
"participant-cc": "file:./packages/participant-cc",
"person-cc": "file:./packages/person-cc",
// BO: magic line here
"#convector-rest-sample/common": "file:./packages/#convector-rest-sample/common"
// EO: magic line here
}
Clean Up
to finish we can remove the legacy files org1.participant.config.json org1.person.config.json org2.person.config.json org2.participant.config.json, now we use the config file chaincode.config.json (thanks for the tip Diego)
# remove legacy files
rm org1.participant.config.json org1.person.config.json org2.person.config.json org2.participant.config.json
Note: don't forget to update packages.json "cc:package" with new ./chaincode.config.json file
package.json
"cc:package": "f() { npm run lerna:build; chaincode-manager --update --config ./chaincode.config.json --output ./chaincode-$1 package; npm run copy:indexes -- $1; }; f",
to finish thanks to all the kindly people of Covalent
UPDATE: in a fresh project/clone if I try to lerna bootstrap I get the below error
$ lerna bootstrap
src/participant.controller.ts:1:35 - error TS2307: Cannot find module '#convector-sample/common'.
1 import { appConstants as c } from '#convector-sample/common';
this is because the package require build before it's used
# build common library: this is required on fresh clones before lerna boostrap
$ npx lerna run build --scope #convector-sample/common --stream
now it works
$ lerna bootstrap
...
lerna success Bootstrapped 6 packages

How to run full-icu with nodemon?

When I try to run full-icu with nodemon, it doesn't add the localization support as expected.
I've tried to include full-icu via an environment variable, which I load with dotenv, then via CLI, none had worked yet. I'm running Node.js 12.3.1, nodemon 1.19.1, full-icu 1.3.0.
CLI:
const gulp = require('gulp');
const {spawn} = require('child_process');
gulp.task('nodemon', () =>
{
const {stdout, stderr} = spawn('nodemon.cmd', ['--icu-data-dir=/node_modules/full-icu']);
//...
});
.env:
NODE_ICU_DATA=/node_modules/full-icu
Also, I've tried to remove the first slash or include the absolute path without any success.
When I run:
console.log(Intl.NumberFormat.supportedLocalesOf('sk');
The output should be ['sk'], but is actually [] for any language other than English.
I've solved it by changing "start" in "scripts" in package.json from:
"scripts": {
"start": "node ./app"
}
to:
"scripts": {
"start": "node --icu-data-dir=node_modules/full-icu ./app"
}
The problem with the CLI was probably a wrong order of the variables because --icu-data-dir was preceded by ./app. And I guess the problem with the environment variable was that I'd added it after the initialization of the process and Node didn't check it afterwards. If that's not the case, I'd like to be corrected.

NPM CLI application relative path doesn't work

I'm creating a CLI application in NodeJS and the package is going to be published on NPM. The application is very simple as it has only two files. Here is the structure of application:
package.json
{
"name": "mycliapp",
"version": "1.0.0",
"description": "Some description",
"main": "./bin/cli.js",
"preferGlobal": true,
"bin": {
"mycliapp": "bin/cli.js"
},
}
bin/cli.js
const nodePlop = require('node-plop');
const configPath = './bin/config.js';
const plop = nodePlop(configPath, {
force: argv.force || argv.f
});
bin/config.js
{
// some configuration
}
Now if I create symlink with npm install -g from this directory and run the command mycliapp from the same development directory, it works absolutely fine but if I run this mycliapp command from any other directory in my computer, the const configPath = './bin/config.js' is tried to be taken from the current working directory not from the actual npm package and hence the config file is not found.
How can I solve this issue? I tried using __dirname and __filename with path.join but nothing seems to be working.
I also published this package on npm and installed from there, the same issue is occurring.
The JSON in your package.json is malformed -- you should remove the trailing comma after you set the bin parameter.
Also, if your configuration file is in the same directory as your script, you should reference it within your script as ./config.js.
Finally, you need to include a shebang (#!/usr/bin/env node) at the top of your cli.js file or any file you intend to use as your point of entry into the app so that your system knows what interpreter to use to execute the file.
See this post on the npm blog for more information.

Passing an argument to npm run script

I am creating a script in npm package.json.
The script will run yeoman to scaffold my template and then I want to run a gulp task to do some more stuff to a specific file (inject using gulp-inject)
The npm task looks like this:
"scaffolt": "scaffolt -g scaffolt/generators template && gulp inject"
Now, i need to be able to call the command from the command line giving a name to my template.
The command I need to run is the following:
npm run scaffolt {templateName}
but if I do this, then I try to run a gulp task called the same as the typed {templateName}.
A quick example: If I run npm run scaffolt myTemplate then the second part of this will try to run a task called gulp myTemplate, failing.
Is there any way to pass the {myTemplate} name as an argument to the second part of the script so that it can be used in the gulptask?
The gulp task currently only console.log the process.argv.
You can pass arguments to the npm run-script. Here is the documentation.
Make gulp tasks for these operations.
//gulpfile.js
const gulp = require('gulp');
const commandLineArgs = require('command-line-args');
const spawn = require('child_process').spawn;
gulp.task('inject', ['scaffolt'], () => {
console.log('scaffolt complete!');
});
gulp.task('scaffolt', (cb) => {
const options = commandLineArgs([{ name: 'templateName' }]);
//use scaffolt.cmd on Windows!
spawn('scaffolt', ['-g', 'scaffolt/generators', options.templateName])
.on('close', cb);
});
And in your package
//package.json
"scripts": {
"scaffolt": "gulp inject "
}
And to run it npm run scaffolt -- --templateName=something
Tip: npm run-script appends node_modules/.bin directory in the PATH so we can spawn executables just like they are on the same folder!
You can use magical $npm_config_<exampleVarName> in script definition and then pass the value of it either from env variable named match exampleVarName or pass it in command line you add --exampleVarName=ValueHere
in your case
//package.json
"scripts": {
"scaffolt": "scaffolt -g scaffolt/generators $npm_config_templateName && gulp inject"
}
then run it as
npm run scaffolt --templateName=whatever

Resources