How quickly check whether to run npm install Node - node.js

Next script goes thought all folders and installs dependencies
var fs = require( "fs" ),
path = require( "path" ),
child_process = require( "child_process" );
var rootPath = "./";
var dirs = fs.readdirSync( rootPath )
.filter( function( dir ) {
return fs.statSync( path.join( rootPath, dir )).isDirectory();
});
var install = function()
{
if ( dirs.length === 0 )
return;
var dir = dirs.shift();
console.log( "installing dependencies for : '" + dir + "'" );
child_process.exec( "npm prune --production | npm install", {
cwd: rootPath + dir
}, install );
};
install();
How to run npm install command only if package.json exists in folder?

Try this command:
ls | grep package.json && (npm prune --production | npm install)
I assume you are running this in Linux.
In theory, if I remember correctly, the ouput of the command ls will be piped to the grep command, and only if the grep command will have found a result, then the commands (npm prune --production | npm install) will be executed.
This is not tested by me at the moment of writting this, since I don't have a Linux box right now to test this, but I hope it works.
UPDATE:
The efficient command, as per Dan's comment would be
test -f package.json && (npm prune --production | npm install)

Related

How to execute npm script using grunt-run?

I have a npm task in my package.json file as follows to execute jest testing:
"scripts": {
"test-jest": "jest",
"jest-coverage": "jest --coverage"
},
"jest": {
"testEnvironment": "jsdom"
},
I want to execute this task npm run test-jest using grunt. I installed grunt-run for the same and added the run task, but how do I invoke this npm task there?
run: {
options: {
// Task-specific options go here.
},
your_target: {
cmd: 'node'
}
}
Configure your Gruntfile.js similar to the example shown in the docs.
Set the value for the cmd to npm.
Set run and test-jest in the args Array.
Gruntfile.js
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-run');
grunt.initConfig({
run: {
options: {
// ...
},
npm_test_jest: {
cmd: 'npm',
args: [
'run',
'test-jest',
'--silent'
]
}
}
});
grunt.registerTask('default', [ 'run:npm_test_jest' ]);
};
Running
Running $ grunt via your CLI using the configuration shown above will invoke the npm run test-jest command.
Note: Adding --silent (or it's shorthand equivalent -s) to the args Array simply helps avoids the additional npm log to the console.
EDIT:
Cross Platform
Using the grunt-run solution shown above failed on Windows OS when running via cmd.exe. The following error was thrown:
Error: spawn npm ENOENT Warning: non-zero exit code -4058 Use --force to continue.
For a cross-platform solution consider installing and utlizing grunt-shell to invoke the npm run test-jest instead.
npm i -D grunt-shell
Gruntfile.js
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt); // <-- uses `load-grunt-tasks`
grunt.initConfig({
shell: {
npm_test_jest: {
command: 'npm run test-jest --silent',
}
}
});
grunt.registerTask('default', [ 'shell:npm_test_jest' ]);
};
Notes
grunt-shell requires load-grunt-tasks for loading the Task instead of the typical grunt.loadNpmTasks(...), so you'll need to install that too:
npm i -D load-grunt-tasks
For older version of Windows I had to install an older version of grunt-shell, namely version 1.3.0, so I recommend installing an earlier version.
npm i -D grunt-shell#1.3.0
EDIT 2
grunt-run does seem to work on Windows if you use the exec key instead of the cmd and args keys...
For cross platform purposes... I found it necessary to specify the command as a single string using the exec key as per the documentation that reads:
If you would like to specify your command as a single string, useful
for specifying multiple commands in one task, use the exec: key
Gruntfile.js
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-run');
grunt.initConfig({
run: {
options: {
// ...
},
npm_test_jest: {
exec: 'npm run test-jest --silent' // <-- use the exec key.
}
}
});
grunt.registerTask('default', [ 'run:npm_test_jest' ]);
};

Yarn install production dependencies of a single package in workspace

I'm trying to install the production dependencies only for a single package in my workspace. Is that possible?
I've already tried this:
yarn workspace my-package-in-workspace install -- --prod
But it is installing all production dependencies of all my packages.
yarn 1 doesn't support it as far as I know.
If you are trying to install a specific package in a dockerfile, then there is a workaround:
copy the yarn.lock file and the root package.json
copy only the packages's package.json that you need: your package and which other packages that your package depends on (locally in the monorepo).
in the dockerfile, manually remove all the devDependnecies of all the package.json(s) that you copied.
run yarn install on the root package.json.
Note:
Deterministic installation - It is recommended to do so in monorepos to force deterministic install - https://stackoverflow.com/a/64503207/806963
Full dockefile example:
FROM node:12
WORKDIR /usr/project
COPY yarn.lock package.json remove-all-dev-deps-from-all-package-jsons.js change-version.js ./
ARG package_path=packages/dancer-placing-manager
COPY ${package_path}/package.json ./${package_path}/package.json
RUN node remove-all-dev-deps-from-all-package-jsons.js && rm remove-all-dev-deps-from-all-package-jsons.js
RUN yarn install --frozen-lockfile --production
COPY ${package_path}/dist/src ./${package_path}/dist/src
COPY ${package_path}/src ./${package_path}/src
CMD node --unhandled-rejections=strict ./packages/dancer-placing-manager/dist/src/index.js
remove-all-dev-deps-from-all-package-jsons.js:
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
async function deleteDevDeps(packageJsonPath) {
const packageJson = require(packageJsonPath)
delete packageJson.devDependencies
await new Promise((res, rej) =>
fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8', error => (error ? rej(error) : res())),
)
}
function getSubPackagesPaths(repoPath) {
const result = execSync(`yarn workspaces --json info`).toString()
const workspacesInfo = JSON.parse(JSON.parse(result).data)
return Object.values(workspacesInfo)
.map(workspaceInfo => workspaceInfo.location)
.map(packagePath => path.join(repoPath, packagePath, 'package.json'))
}
async function main() {
const repoPath = __dirname
const packageJsonPath = path.join(repoPath, 'package.json')
await deleteDevDeps(packageJsonPath)
await Promise.all(getSubPackagesPaths(repoPath).map(packageJsonPath => deleteDevDeps(packageJsonPath)))
}
if (require.main === module) {
main()
}
It looks like this is easily possible now with Yarn 2: https://yarnpkg.com/cli/workspaces/focus
But I haven't tried myself.
Here is my solution for Yarn 1:
# Install dependencies for the whole monorepo because
# 1. The --ignore-workspaces flag is not implemented https://github.com/yarnpkg/yarn/issues/4099
# 2. The --focus flag is broken https://github.com/yarnpkg/yarn/issues/6715
# Avoid the target workspace dependencies to land in the root node_modules.
sed -i 's|"dependencies":|"workspaces": { "nohoist": ["**"] }, "dependencies":|g' apps/target-app/package.json
# Run `yarn install` twice to workaround https://github.com/yarnpkg/yarn/issues/6988
yarn || yarn
# Find all linked node_modules and dereference them so that there are no broken
# symlinks if the target-app is copied somewhere. (Don't use
# `cp -rL apps/target-app some/destination` because then it also dereferences
# node_modules/.bin/* and thus breaks them.)
cd apps/target-app/node_modules
for f in $(find . -maxdepth 1 -type l)
do
l=$(readlink -f $f) && rm $f && cp -rf $l $f
done
Now apps/target-app can be copied and used as a standalone app.
I would not recommend it for production. It is slow (because it installs dependencies for the whole monorepo) and not really reliable (because there may be additional issues with symlinks).
You may try
yarn workspace #my-monorepo/my-package-in-workspace install -- --prod

Running npm install with sbt

So I have a sbt project that uses sbt-js-engine and sbt-webpack plugins.
It successfully gets and resolves npm packages just fine. And then webpack would build the project.
I have added a npm install script into package.json like so,
"scripts": {
"install": "bower install"
}
However, the problem I am currently having is that when I run webpack (which intern uses sbt-js-engine ) it runs npm update instead of npm install.
Heres an excerpt of my build.sbt,
lazy val common = project.in(file("common")).
enablePlugins(SbtWeb).
settings(
sourceDirectory in webpack := baseDirectory.value,
resourceManaged in webpack := (resourceManaged in webpack in root).value,
includeFilter in webpack := ("*.jsx" || "*.js" || "*.json") && new FileFilter {
#tailrec
override def accept(pathname: File): Boolean = {
if (pathname == null) false
else if (pathname.getName == "javascripts") true
else accept(pathname.getParentFile)
}
},
JsEngineKeys.engineType := JsEngineKeys.EngineType.Node
)
Is there anyway I could run npm install instead or even before as a depedency for webpack task ?
You could try something like this:
sourceDirectory in webpack := {
Process("/usr/local/bin/npm install", file("[path to working dir]")).!
baseDirectory.value
}
That would mean it would run at same time as setting the webpack settings.

How do I npm install for current directory and also subdirectories with package.json files?

I have a an application which is a web game server and say for example I have node_modules which I use in directory ./ and I have a proper package.json for those. it happens that in directory ./public/ I have a website being served which itself uses node_modules and also has a proper package.json for itself.
I know I can do this by navigating the directories. But is there a command or way to automate this so that it is easier for other developers to bootstrap the application in their system?
Assuming you are on Linux/OSX, you could try something like this:
find ./apps/* -maxdepth 1 -name package.json -execdir npm install \;
Arguments:
./apps/* - the path to search. I would advise being very specific here to avoid it picking up package.json files in other node_modules directories (see maxdepth below).
-maxdepth 1 - Only traverse a depth of 1 (i.e. the current directory - don't go into subdirectories) in the search path
-name package.json - the filename to match in the search
-execdir npm install \; - for each result in the search, run npm install in the directory that holds the file (in this case package.json). Note that the backslash escaping the semicolon has to be escaped itself in the JSON file.
Put this in the postinstall hook in your root package.json and it will run everytime you do an npm install:
"scripts": {
"postinstall": "find ./apps/* -name package.json -maxdepth 1 -execdir npm install \\;"
}
For cross-platform support (incl. Windows) you may try my solution.
Pure Node.js
Run it as a "preinstall" npm script
const path = require('path')
const fs = require('fs')
const child_process = require('child_process')
const root = process.cwd()
npm_install_recursive(root)
function npm_install_recursive(folder)
{
const has_package_json = fs.existsSync(path.join(folder, 'package.json'))
if (!has_package_json && path.basename(folder) !== 'code')
{
return
}
// Since this script is intended to be run as a "preinstall" command,
// skip the root folder, because it will be `npm install`ed in the end.
if (has_package_json)
{
if (folder === root)
{
console.log('===================================================================')
console.log(`Performing "npm install" inside root folder`)
console.log('===================================================================')
}
else
{
console.log('===================================================================')
console.log(`Performing "npm install" inside ${folder === root ? 'root folder' : './' + path.relative(root, folder)}`)
console.log('===================================================================')
}
npm_install(folder)
}
for (let subfolder of subfolders(folder))
{
npm_install_recursive(subfolder)
}
}
function npm_install(where)
{
child_process.execSync('npm install', { cwd: where, env: process.env, stdio: 'inherit' })
}
function subfolders(folder)
{
return fs.readdirSync(folder)
.filter(subfolder => fs.statSync(path.join(folder, subfolder)).isDirectory())
.filter(subfolder => subfolder !== 'node_modules' && subfolder[0] !== '.')
.map(subfolder => path.join(folder, subfolder))
}

Aborted (core dumped) npm ERR! code ELIFECYCLE (upgrading from node 9 -> node 10)

I am trying to build a docker image containing my Vue.js project but it fails with below error when I try to upgrade from Node 9 to Node 10:
$ docker build -t frontend-image .
...
Step 10/10 : RUN npm run build
---> Running in a7b7f92e4615
> demo-vue#1.0.0 build /app
> node build/build.js
node[18]: ../src/node_file.cc:836:void node::fs::Stat(const v8::FunctionCallbackInfo<v8::Value>&): Assertion `(argc) == (4)' failed.
1: 0x9d8da0 node::Abort() [node]
2: 0x9d8e27 [node]
3: 0x9e5652 [node]
4: 0xba3c19 [node]
5: 0xba5a07 v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node]
6: 0x13726d9 [node]
Aborted (core dumped)
npm ERR! code ELIFECYCLE
npm ERR! errno 134
npm ERR! demo-vue#1.0.0 build: `node build/build.js`
npm ERR! Exit status 134
npm ERR!
npm ERR! Failed at the demo-vue#1.0.0 build 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! /root/.npm/_logs/2019-10-23T19_00_49_014Z-debug.log
The command '/bin/sh -c npm run build' returned a non-zero code: 134
Some details below...
Project layout:
frontend/
-> build/
-> config/
-> src/
-> static/
-> test/
-> build.sh
-> Dockerfile
-> package.json
frontend/Dockerfile:
FROM node:10
# Works with node 9
#FROM node:9
RUN apt install curl
# make the 'app' folder the current working directory
RUN mkdir /app
WORKDIR /app
# copy both 'package.json' and 'package-lock.json' (if available)
COPY package*.json ./
# copy project files and folders to the current working directory (i.e. 'app' folder)
COPY . .
# install simple http server for serving static content and project dependencies
RUN npm install -g http-server
RUN npm install
EXPOSE 8080
# PROD build
# build app for production with minification
RUN npm run build
frontend/package.json (scripts part):
"scripts": {
...
"build-server": "webpack --config build/webpack.server.conf.js && node src/server.js",
"lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs",
"build": "node build/build.js && npm run build-server"
},
frontend/build/build.js:
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // if you are using ts-loader, setting this to true will make tyescript errors show up during build
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
frontend/build/check-versions.js
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
Looking at the above error message this part:
> node build/build.js
seems to be the cause of the error but the following text does not really provide me with much help to why node build/build.js fails.
Any ideas?

Resources