Problem when using process.cwd() in published npm package - node.js

I'm dipping my toes into cli tooling by building a simple program for automating Gerrit commits. Everything works locally, but after publishing the package to npm and installing it globally it looks like process.cwd() behaves differently. The program exits, but no console.log(). Even a simple console.log(process.cwd()) is ignored (again works locally). Is it possible to use process.cwd() when running a globally installed npm package?
console.log(process.cwd());
const getCurrentBranchName = (p = process.cwd()) => {
const gitHeadPath = `${p}/.git/HEAD`;
return fs.existsSync(gitHeadPath)
? fs.readFileSync(gitHeadPath, "utf-8").trim().split("/").pop()
: (console.log("not a git repo"), process.exit(0));
}
const currentBranch = getCurrentBranchName();
When ran locally (with node index):
$ /Users/jpap/.npm-packages/lib/node_modules/gittest
$ not a git repo

You haven't proved the problem is with process.cwd()
The code in your question and the results your describe only indicate that the console.log() calls aren't executing.
You could easily test this by adding the following to the top of your code:
console.log('My name is Inigo Montoya. You killed my father. Prepare to die!')
What you are publishing is likely not the same as what you are running locally
For example, you could be using a bundler (e.g. Rollup) configured to strip console.log calls.
This is easy to confirm. Simple look at the code of the npm installed version:
Use npm root -g to find out where your global packages are installed. For non-global packages, look in node_modules.
Find your package's subdir.
Look at the code, or diff it with the source code.
I suspect you will see all console.log statements removed.

Related

Install different packages depending on node version

I have a project that depends on the websocket package. However, for node 10.x, the latest version (1.0.31) of websocket works, while on node 4.x version 10.0.24 works but the 10.0.31 does not. Is it possible to specify different package (versions) per nodejs version to handle cases like this, e.g. like so
node 4.x and older -> websockets 1.0.24
all other node versions -> websockets 1.0.31
Preferable it should work in both npm and yarn, but if it only works in either that's fine as well.
The node 10 version is used in dev setups, while the node 4.x is used in a legacy embedded platform that cannot run docker or be upgraded.
Consider utilizing a postinstall script in the scripts section of your projects package.json. For instance:
package.json
"scripts": {
"postinstall": "node install-websocket"
},
As you can see, the postinstall script invokes a nodejs script, arbitrarily named install-websocket.js.
install-websocket.js
const execSync = require('child_process').execSync;
const nodeMajorVersion = process.version.replace(/^v/, '').split('.')[0];
const websocketVersion = nodeMajorVersion <= '4' ? '1.0.24' : '1.0.31';
execSync('npm install websocket#' + websocketVersion, {
cwd: __dirname,
stdio: 'inherit'
});
The install-websocket.js script essentially performs the following tasks:
Gets the version of node.js using process.version which returns a string, e.g. v13.10.1
To obtain the Major version from that string (i.e. 13 in that aforementioned example) we use a combination of the replace() and split() methods.
The conditional (ternary) operator ascertains
which version of websocket to subsequently install - based on whether the value of nodeMajorVersion is <= 4.
Finally we "shell out" the appropriate npm install websocket#x.x.x command using execSync.
Note: If you're concerned about execSync being synchronous, then utilize the asynchronous exec instead.
Additional Notes:
Given the code shown above it assumes the install-websocket.js file resides in the root of your project directory, i.e. at the same level as package.json.
my-project
├── package.json
├── install-websocket.js
└── ...
It's important for the install-websocket.js file to exist at this location for following two reasons:
Primarily, and most importantly, because you'll have noticed that we specify __dirname for the value of execSync's cwd option. The value of __dirname in this context is the pathame to the parent directory of wherever the install-websocket.js file resides.
Essentially by setting the cwd option to this specific pathname, (i.e. the path to the project directory), we ensure that when the npm install websocket#x.x.x command is run it gets installed in the same location as where your project resides - regardless of whether it's installed locally or globally.
The postinstall script in package.json expects the install-websocket.js file to reside there too. Note how it currently runs; node install-websocket, and doesn't assume the file exists elsewhere, i.e. it's not running something like: node ./some/path/to/install-websocket
If consumers of your package have npm configured to ignore-scripts then websocket's simply will not be installed because the postinstall script will not be invoked.

Specify the path to node when running executable npm moduels

I'm fairly new to node and npm so imagine this might be the way I'm trying to use it. My understanding so far and questions are as follows.
When installing modules with npm if they have executables they are created in node_moadules/.bin
For example npm install istanbul creates
node_moadules/.bin/istanbul.cmd with the following contents
#IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\istanbul\lib\cli.js" %*
) ELSE (
#SETLOCAL
#SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\istanbul\lib\cli.js" %*
)
This seems to be roughly similar for other npm modules installed.
1) When would there be a node.exe in the bin directory (%~dp0 is the path of the batch file - so node_moadules/.bin)? When using npm istanbul it will use the above script but I don't understand when it would use the IF block, it seems it would always go into the ELSE which uses node on the path to run it. This will work as intended when I have node installed and on my path. However this would seem to be a problem if you don't have it on your path or need to use a specific version in a project (different from the version on your path).
2) I can fix this by manually editing the above script to point to the specific node version but don't want to have to do this for all module executables installed with npm. Is there a way to configure this in npm so it uses a specific path to node when it generates these executable files?
A bit more background
We're using the gradle-node-plugin in our build so we don't have to manage node and npm on our build machines, this does it for us. However this means we don't have node on the path.
The plugin allows me to install npm modules but there is no clear way to then run the executables, using the version of node unpacked by the plugin, without manually editing the generated executable scripts as in 2), or instead of calling npm istanbul calling something like this:
<path_to>\node <path_to>\node_modules\istanbul\lib\cli.js
This also doesn't seem right as I'm hardcoding to the cli.js executable path within the module which could presumably change in future versions of that module.
If anyone could explain this, or tell me where the specific part of the docs that do are that would be helpful. I've tried to search but probably not using the right terms.

Grunt only runs default task

Using OSX Maveriks, iTerminal, zsh.
Problem: Grunt runs only the default task regardless of the task you specify.
Problem: It only happens in 1 computer.
What can be the reason for grunt not to run but the default task. I've been working with Grunt for a long time now and we use it at the company I work at. Everything has been working perfectly smooth except for 1 single coworker that when he runs grunt myTask is like if he were running just grunt, even if I run a made up task like grunt myNotExistentTask it ignores it and again looks like it runs grunt.
I did check for stupid errors:
Checked that I'm accessing the same project folder (multiple times).
I deleted the default task to see if it complained and it does.
I checked that the Gruntfile was not throwing errors. Then I forced to throw errors to see if it would complain, and it does.
Runned npm install.
Check both bashrc and zshrc for a grunt command or something.
Obviously grunt is accessible.
Runned it in the default terminal and in iTerm.
"Have you try to turn it on and off?" Yes I did restar the laptop.
I tried with multiple coworkers laptops and it only happens in the one laptop.
I did not have time to (but I will check for these):
test creating a simple Gruntfile somewhere else in his laptop, but I have a weird feeling that it would behave the same way.
reinstall grunt. Would that help?
grunt --version or others for that matter, I assumed that it has the specified version in the package.json.
we are using:
"grunt-cli": "^0.1.13",
"grunt": "^0.4.4"
The code is not causing the issue here because every computer runs it but one. I can't show the actual code but I can give you the boilerplate just in case there is some mysterious thing I don't know about:
module.exports = function(grunt) {
grunt.initConfig({
pkg : grunt.file.readJSON("package.json"),
someTask : {
options : {
},
target1 : {
}
}
});
grunt.loadNpmTasks("dependency1");
grunt.loadNpmTasks("dependency2");
grunt.loadNpmTasks("etc");
grunt.registerTask("default", function () {
// creates a server with one of the dependencies
});
grunt.registerTask("w", function () {
// creates a watch with one of the dependencies
});
};
There is a reason behind why we are not simply doing something like grunt.registerTask("default", [".."]). Basically, our projects are too big and we need to change settings for each task to increase performance (e.g. not to watch too many files, etc).
I googled this different ways and couldn't find anything about this happening before. Any ideas are welcomed.
Edit
I forgot to mention that he has been working with the project for a while, we just recently noticed because he never needed to use any other task but the default task until a few days ago when he needed to use a different task, in summary, we don't know when or why it started.
After:
Deleting and cloning the repo.
Deleting npm_modules.
Reinstalling everything.
Creating an isolated project to test this issue.
and failing, we checked for the paths to grunt thanks to a comment by #XavierPriour
Using zsh with iTerm:
➜ folder git:(branch) which grunt
/usr/local/bin/grunt
➜ folder git:(branch) type -a grunt
grunt is /usr/local/bin/grunt
grunt is /usr/bin/grunt
We deleted the extra grunt binary:
➜ folder git:(branch) cd /usr/bin
➜ bin sudo -rm grunt
And everything started working correctly. I assume the issue is he installed grunt globally instead of just grunt-cli, although I'm not sure.
The most popular answer to this question shows the dos and don'ts of how to install grunt.

Ember-cli: Inject version into app

I like to display my project version (from package.json), the git commit hash and working-copy status in the footer of my Ember app built using ember-cli and broccoli.
I can grab the version prefix easily enough by adding to my config/environment.js:
ENV.APP.version = require('../package.json').version
I was using grunt-git-describe previously to grab the revision hash and dirty/clean status. How can I do something similar in ember-cli?
Simply shelling out to git describe is problematic since child_process does not have a synchronous method of executing a shell command.
It there a way I can return a promise from somewhere and prevent config/environment.js from resolving until my async git describe completes?
The npm packages exec-sync and execSync don't seem to work well for me on Windows.
ember-git-version is an ember-cli addon that provides the current revision hash.
After installing the node package, the config/environment hash will have a property currentRevision. See initializers/print-git-info.js for how to access it from your app.
You can use the exec-sync package and then add something like this to Brocfile.js:
var execSync = require('exec-sync'),
gitVersion = execSync('git describe');
fs.writeFileSync('app/version.js', 'App.version = "' + gitVersion + '";';
You'll then need to import that somewhere.

Can I write npm package in CoffeeScript?

I have used CoffeeScript for a while. Now I need to write a npm package, can I write it in CoffeeScript, or I should compile CoffeeScript into JavaScript?
I'm going to suggest that you write your package in coffeescript, but only publish it in javascript. I do it like this:
coffeescript code goes in src
code is compiled to lib
src is committed to my git repo, lib is in my .gitignore
lib is published to npm, src is in my .npmignore
the coffee-script package is in my devDependencies
You can take a look at a simple package of mine, refix, for inspiration:
https://github.com/linus/refix
npm install refix
You can write NPM modules in coffeescript, but in order for them to be usable by JS users they must be compiled to JS before you publish on NPM.
package.json makes this easy with their prepublish script hook which runs the specified script before you publish. Heres an example of a prepublish NPM hook in zombie.js
https://github.com/assaf/zombie/blob/master/package.json#L16
If a lot of your modules have coffee-script in their devDependencies, it's useful to just globally install coffee-script instead of install it for each module (which takes much longer).
coffee-build is a global version manager for coffee-script.
Just add these 2 scripts to your package.json:
{
"name": "my-coffee-module",
"scripts": {
"build": "coffee-build -v 1.11.x -b -o js src",
"postinstall": "npm run build"
}
}
Notice how -v 1.11.x is not an exact version, which allows implicit upgrades.
The only downfall is that users must npm install -g coffee-build before they can install your module.
I have written npm packages in CoffeeScript from scratch. I encourage you to use CoffeScript for node as well as for the Browser. However, before you can use or publish your module, you have to compile the source CoffeeScript to JavaScript. That should not hold you back from using CoffeeScript, though.
Tip: While developing, use coffee -cw yourfile.coffee (command line) to watch the file for changes and compile on save.
While I'm not sure if it's the best approach, technically it is possible to write your package mostly in CoffeeScript.
Basically, you can write a JS file that simply wraps the coffee command, like so:
bin/howl.coffee
console.log 'Awwwooooo!'
bin/howl.js
#!/usr/bin/env node
var path = require('path');
var exec = require('child_process').exec;
var coffee = path.resolve(__dirname, '../node_modules/coffee-script/bin/coffee');
var howl = path.resolve(__dirname, './howl.coffee');
var command = coffee + ' ' + howl;
exec(command, function(error, stdout) {
if (error) { throw error };
console.log(stdout);
});
Running node howl.js (or simply howl when it's installed globally) will now output Awwooooo!. You can do things like require other CoffeeScript files and access arguments by passing them from the JavaScript "wrapper" to the CoffeeScript.
Anyway, there may be reasons not to do this, but it has worked for me so far so figured I'd submit this for an additional perspective.
For a simple example project using this technique, check out https://www.github.com/joshuabc/packdown.

Resources