How to use .ENV variables when publishing npm package - node.js

How do I inject my environment variable into the final build that gets published to npm?
I'm using the dotenv package and that works great locally. However when I do npm publish, the env variable is gone since it just runs the file in the main property of the package.json.
Some relevant fields from package json:
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
"files": [
"lib",
".env"
],
"scripts": {
"start" : "tsc && node -r dotenv/config ./lib/index.js"
},
"bin" : {
"run-app" : "./lib/index.js"
},
"dependencies": {
"dotenv": "^8.2.0",
}
.gitignore:
# OSX
.DS_Store
node_modules
notes.MD
lib
.env
I tried to inject my env variable by passing it to my start script and also call the .config() method on dotenv inside the code. Either way the env variable ends up undefined when running my package using npx.
What am I doing wrong?

Related

Npm workspaces - call workspace script from root package

I'm struggling with multiple npm packages in a root git repository with custom dev scripts to handle launch, compile, build and so on. Now I came across npm workspaces and wanted to use this stunning new feature in my following project structure but I can't get it to work:
projectx (root)
- package.json
- apps
-- backend
-- src
-- package.json (name: #projectx/backend, scripts: "dev":"ts-node or whatever")
-- common
-- src
-- package.json (name: #projectx/common)
-- frontend
-- src
-- package.json (name: #projectx/frontend, scripts: "dev":"webpack")
My root package.json contains:
{
"name": "packagex",
"version": "1.0.0",
"description": "",
"main": "index.js",
"private": "true",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"back:dev": "npm workspace #projectx/backend dev",
"front:dev": "npm workspace #projectx/frontend dev",
"dev": "run-p back:dev front:dev"
},
"workspaces": [
"apps/*"
],
"repository": {
"type": "git",
"url": "git_url"
},
"author": "me",
"license": "ISC",
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}
And now I want to start backend and frontend with npm-run-all and the command on root: npm run dev which results in:
And I also want to share the common package with backend and frontend, which should be possible in this case. Maybe anobody else is facing the same problem or has some ideas what I am doing wrong here.
npm#7.7.0 added a way to call scripts from child packages/workspaces, here are some examples based on your original:
Running a script named "dev" in all workspaces located under apps/backend:
npm run dev -w apps/backend
Running a script named "dev" in all workspaces:
npm run dev --ws
Running a script named "dev" in a package named #projectx/frontend:
npm run dev -w #projectx/frontend
More info:
Related CHANGELOG entry: https://github.com/npm/cli/releases/tag/v7.7.0
Docs: https://docs.npmjs.com/cli/v7/commands/npm-run-script#workspaces-support
Blog post: https://dev.to/ruyadorno/npm-workspaces-npm-run-and-exec-1lg0
Your "workspaces" property in package.json looks right. I'm using NPM Workspaces and it's working well, but it's still missing a lot of features so you need to wire things up yourself. I also don't think npm worksace is a command (but maybe for the future?), so here's a checklist to get it to work:
Make sure you're using Node 15+ and NPM 7+
Set all package.json to "private": true,
Delete all package-lock.json inside of your project, go to the root, then npm install. It should generate one root level package-lock.json that contains all dependencies for your workspaces
Since you're using npm-run-all, add this to your scripts:
"scripts": {
"back:dev": "cd apps/backend && npm run dev",
"front:dev": "cd apps/fontend && npm run dev",
"dev": "npm-run-all build --parallel back:dev front:dev"
}
Then start it with npm run dev.
Note, you may want to consider using start scripts instead of dev to shorten the command you need to type (e.g. npm start instead of npm run dev), but npm run dev will still be fine.
In root package.json you can also add short name for each package:
"scripts": {
"api": "npm --workspace=#app/api run",
}
#app/api is a name in package.json
And run scripts in ./packages/api folder from root like so:
npm run api lint
npm run api dev
I think you wish to:
keep scripts and dependencies separate (thus the 4 package.json files), for ease of maintenance
May I suggest a work-around without workspaces that might do what you're after:
{
...
"scripts": {
"//back:dev": "npm workspace #projectx/backend dev",
"back:dev": "npm --prefix apps/backend dev",
"//front:dev": "npm workspace #projectx/frontend dev",
"front:dev": "npm --prefix apps/frontend dev",
"dev": "run-p back:dev front:dev"
},
"//workspaces": [
"apps/*"
],
"devDependencies": {
"#local/back": "file:apps/backend",
"#local/front": "file:apps/frontend",
"npm-run-all": "^4.1.5"
}
}
The npm --prefix runs npm scripts in another folder than the current one.
The #local/back dependencies are not necessary for that, but I've found such useful if eg. a package depends on another. You might use that trick to reach for the common by:
"dependencies": {
"#local/common": "file:../common"
}
I wished a week ago that workspaces would offer a better solution, but didn't find any benefit over the above mechanisms.
I would also like workspaces to:
only expose those files in the files entry of the particular package.json (now, all are shown)
only allow import to paths in the exports of the particular package.json, if it has one
See
NPM Workspaces monorepo - share local package's distribution folder as root instead of the entire source files

Generic Node.js $INIT_CWD for Windows and *nix

How can I replace $INIT_CWD in a Node.js run script with something "generic" that also works on Windows?
package.json in root
{
"name": "foo",
"version": "2.0.0",
...
"scripts": {
..
"start": "live-server $INIT_CWD/foobar --port=8080"
}
}
Test
This works fine on Linux and macOS; serves files from test/foobar.
$ cd test
$ npm start
However, on Windows it would have to be %INIT_CWD% instead of $INIT_CWD.
How can I modify package.json to be OS-agnostic?
The/one solution is to use cross-env-shell from https://www.npmjs.com/package/cross-env.
Run scripts that set and use environment variables across platforms
"devDependencies": {
"cross-env": "^6.0.3"
},
"scripts": {
"start": "cross-env-shell live-server $INIT_CWD/foobar --port=8080"
}

Node.js spawn process not working inside Docker container

I have a server.js file in Node that calls python scripts as follows:
// call python scripts
var spawn = require("child_process").spawn;
var process = spawn('python',["test.py", function_args]);
process.stdout.on('data', function (data){
res.json({
"answer" : from_python
})
});
This works perfectly when simply running node as usual:
node server.js
But when I place everything inside a docker container, the application never enters the process.stdout.on
Everything else works perfectly. I can serve static files, call express endpoints, etc. It is just the process that is not getting called.
I have tried placing child_process inside my package.json file as a dependency.
Here is my Dockerfile:
FROM node:carbon
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
COPY package*.json ./
RUN npm install
# Bundle app source
COPY . .
EXPOSE 80
CMD ["npm","start"]
This is my package.json file:
{
"name": "my_app_name",
"version": "1.0.0",
"description": "my_desc",
"author": "my_name",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.16.2",
"path": "^0.12.7",
"fs": "^0.0.1-security",
"child_process": "^1.0.2"
}
}
Note that running server.js is not the problem. This file runs the other pieces such as serving static files and exposing express endpoints. The problem seems to just be with the child_process not running. It is not that Python isn't getting called, it is that node will not even enter the process part of server.js.
Also note that the Python prints its output via stdout. Not sure of docker has an issue registering standard outputs.
I think
FROM node:carbon
should be
FROM node:9.3
Carbon is the LTS (currently still 8..). You are using libraries from latest v9.3.0.

Setup Babel/Browserify build for (private) npm module

I have an npm module my_lib that is providing some objects I would like available for use by several downstream modules. my_lib is written in ES6, and uses babel to transpile. It lives in a private github repo accessible to the building hosts of the downstream modules. It is important that the downstream modules consuming my_lib are able to import symbols from my_lib like any other node module.
I would like to be able to write import { libA, libB } from 'my_lib'; in any of the downstream modules and have libA defined as expected by my_lib, assuming that downstream module's package.json includes in dependencies an entry like "my_lib": "orgname/reponame#branchname"
I have an entry in my_lib package.json under scripts like so :
"postinstall" : "npm run build",
"build": "mkdir ./dist && browserify ./src/index.js -t babelify -t envify -o index.js",`
where ./src/index.js is
// Entry point for bundling
export * as libB from "../sublib1/adapter";
export * as libA from "../sublib2/adapter";
If necessary, I could call import {libA, libB} from my_lib/dist/bundle, though this is somewhat undesirable. The main thing is to make all the names I'm exporting from my_lib available in the downstream modules. I'm probably confused about what's going on with browserify and babel. I'm perfectly happy to change the postinstall/build hooks to whatever form is more convenient.
Currently, in the downstream package (an express.js app), I have the line import * as adapters from my_lib; Running npm install on the express app completes without error. I see the index.js file I expect in node_modules/my_lib. When I break in my app after the import (or require for that matter), the adapters object exists, but none of the libB or libA on the object. It seems to be a pure vanilla JS object with no members but __proto__.
Normally in this case i will just use babel and avoid browserify. I use something like this in package.json:
{
"main": "dist/index.js",
"scripts": {
"postinstall": "babel src/ -d dist/"
},
"dependencies": {
"babel-cli": "^6.9.0",
"babel-preset-es2015": "^6.9.0"
}
}
Notice that the main entry point for the package is pointing to dist and not to src. This is to make import my_lib and require('my_lib') (both work well) to use the compiled version of the library.
Additionally this is the content of the .babelrc file that it is side by side with package.json
{
"presets": ["es2015"]
}
I use babel and browserify together.
My package.json
{
"name": "my-project",
"version": "1.0.0",
"scripts": {
"build:js": "watchify assets/src/js/main_1.js -o 'exorcist assets/dist/js/main_1.js.map > assets/dist/js/main_1.js' -d -t [babelify --presets [latest]]",
"build:scss": "node-sass -w -r assets/src/scss/ -o assets/dist/css/",
"build": "npm run build:scss & npm run build:js"
},
"devDependencies": {
"babel-cli": "^6.0.0",
"babel-preset-latest": "^6.16.0",
"babelify": "^7.3.0",
"browserify": "^13.1.1",
"exorcist": "^0.4.0",
"node-sass": "^4.5.0",
"watchify": "^3.7.0"
},
"browserify": {
"transform": [
"babelify"
]
}
}
Actual version of package.json: https://gist.github.com/artamonovdev/5f24aaca504e4d1b299bba0413f0a57d

How do I set the default test command for `npm init`?

I know I can do:
npm config set init.author.email me#mycompany.com
npm config set init.license UNLICENSED
To set the defaults used to create a new package.json with npm init. But how can I set the default value for the test command? I've tried
npm config set init.scripts.test "mocha"
But it doesn't work. The npm docs don't seem to help.
Is there a list of all the init defaults?
There is a list of all config defaults npm config list -l.
As you can see there isn't anything like init.scripts. But there is another way to do it. If you first npm install mocha and then npm init you will get a package.json like:
{
"name": "asd",
"version": "1.0.0",
"description": "",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"mocha": "^2.4.5"
},
"scripts": {
"test": "mocha"
},
"author": "",
"license": "UNLICENSED"
}
Although you can't (currently) configure default scripts via npm settings, you can customise the entire npm init output by providing your own init-module. The default is stored at ~/.npm-init.js - whatever you export becomes the result of running npm init.
To make this useful, you'd probably want to hijack the existing default npm init module, which lives in <your npm install directory>/node_modules/init.js. If you only care about providing a default test script, the simplest option is to use init-module, which provides a configuration parameter init-scripts-test.
I was searching for a way to programmatically create a package.json with dynamically defined values. I noticed creating a package.json file before calling npm init -y works as expected.
// Require: fs
const fs = require('fs')
// Variables
const values = {
"scripts": {
"test": "mocha"
}
// as well as any other values you want
}
// Create package.json
fs.writeFileSync('package.json', values)
Running npm init -y after running the above code would generate a package.json with scripts.test = "mocha" defined.
If you're looking for a way to run npm init -y from within code as well, I recommend using shelljs: shell.exec('npm init -y').
npm config set init ... will create a default configuration for all future projects that will be initialized - if you want to just set a few values once - simply use a shellscript to write some values prior to npm init like so:
echo '{"version":"0.1.0","license":"UNLICENSED","private":true,"scripts":{"test":"npx jest"}}' > "./package.json" && npm init -y

Resources