npm packages
ネストされたサブフォルダにインストールする最も正しい方法は何ですか?
my-app
/my-sub-module
package.json
package.json
しているための最良の方法は何であるpackages
で/my-sub-module
たときに自動的にインストールさnpm install
で実行されますがmy-app
?
npm packages
ネストされたサブフォルダにインストールする最も正しい方法は何ですか?
my-app
/my-sub-module
package.json
package.json
しているための最良の方法は何であるpackages
で/my-sub-module
たときに自動的にインストールさnpm install
で実行されますがmy-app
?
回答:
ネストされたサブフォルダーにnpmパッケージをインストールする単一のコマンドを実行する場合は、ルートディレクトリでnpm
and main package.json
を介してスクリプトを実行できます。スクリプトはすべてのサブディレクトリにアクセスして実行しnpm install
ます。
以下は.js
、望ましい結果を達成するスクリプトです。
var fs = require('fs')
var resolve = require('path').resolve
var join = require('path').join
var cp = require('child_process')
var os = require('os')
// get library path
var lib = resolve(__dirname, '../lib/')
fs.readdirSync(lib)
.forEach(function (mod) {
var modPath = join(lib, mod)
// ensure path has package.json
if (!fs.existsSync(join(modPath, 'package.json'))) return
// npm binary based on OS
var npmCmd = os.platform().startsWith('win') ? 'npm.cmd' : 'npm'
// install folder
cp.spawn(npmCmd, ['i'], { env: process.env, cwd: modPath, stdio: 'inherit' })
})
これは、特にモジュール化されたプロジェクト構造(ネストされたコンポーネントとファイルを含む)に対処するStrongLoopの記事の例です。node.js
package.json
提案したように、bashスクリプトでも同じことを実現できます。
編集:Windowsでコードを機能させる
ネストされたサブディレクトリの名前がわかっている場合は、ポストインストールを使用することをお勧めします。でpackage.json
:
"scripts": {
"postinstall": "cd nested_dir && npm install",
...
}
"postinstall": "cd nested_dir2 && npm install"
、各フォルダーのように、package.json内に次のフォルダーを配置することはできませんか?
@Scottの回答によれば、サブディレクトリ名がわかっている限り、install | postinstallスクリプトが最も簡単な方法です。これは、複数のサブディレクトリに対して実行する方法です。例えば、我々が持っているふりapi/
、web/
およびshared/
monorepoルートでサブプロジェクト:
// In monorepo root package.json
{
...
"scripts": {
"postinstall": "(cd api && npm install); (cd web && npm install); (cd shared && npm install)"
},
}
( )
サブシェルを作成し、を回避するためのの適切な使用cd api && npm install && cd ..
。
npm install
、トップレベルで:"(cd was unexpected at this time."
私の解決策は非常に似ています。純粋なNode.js
次のスクリプトは、各サブフォルダーpackage.json
が存在しnpm install
、それぞれで実行されている限り、すべてのサブフォルダーを(再帰的に)検査します。それに例外を追加することができますpackage.json
。以下の例では、そのようなフォルダーの1つは「パッケージ」です。「プレインストール」スクリプトとして実行できます。
const path = require('path')
const fs = require('fs')
const child_process = require('child_process')
const root = process.cwd()
npm_install_recursive(root)
// Since this script is intended to be run as a "preinstall" command,
// it will do `npm install` automatically inside the root folder in the end.
console.log('===================================================================')
console.log(`Performing "npm install" inside root folder`)
console.log('===================================================================')
// Recurses into a folder
function npm_install_recursive(folder)
{
const has_package_json = fs.existsSync(path.join(folder, 'package.json'))
// Abort if there's no `package.json` in this folder and it's not a "packages" folder
if (!has_package_json && path.basename(folder) !== 'packages')
{
return
}
// If there is `package.json` in this folder then perform `npm install`.
//
// 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.
// Hence the `folder !== root` condition.
//
if (has_package_json && folder !== root)
{
console.log('===================================================================')
console.log(`Performing "npm install" inside ${folder === root ? 'root folder' : './' + path.relative(root, folder)}`)
console.log('===================================================================')
npm_install(folder)
}
// Recurse into subfolders
for (let subfolder of subfolders(folder))
{
npm_install_recursive(subfolder)
}
}
// Performs `npm install`
function npm_install(where)
{
child_process.execSync('npm install', { cwd: where, env: process.env, stdio: 'inherit' })
}
// Lists subfolders in a folder
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))
}
人々がこの質問に遭遇した場合の参考のために。今はできる:
npm install --save path/to/my/subfolder
mkdir -p a/b ; cd a ; npm init ; cd b ; npm init ; npm install --save through2 ;
ちょっと待ってください。依存関係を手動で "b"にインストールしただけです。これは、新しいプロジェクトを複製したときに起こることではありません。rm -rf node_modules ; cd .. ; npm install --save ./b
。次にnode_modulesをリストし、次にbをリストします。
ユースケース1:各サブディレクトリ(各package.jsonがある場所)からnpmコマンドを実行できるようにする場合は、を使用する必要がありますpostinstall
。
npm-run-all
とにかくよく使うので、簡潔に保つために使用します(ポストインストールの一部):
{
"install:demo": "cd projects/demo && npm install",
"install:design": "cd projects/design && npm install",
"install:utils": "cd projects/utils && npm install",
"postinstall": "run-p install:*"
}
これには、一度に、または個別にインストールできるという追加の利点があります。これが必要ない場合、またはnpm-run-all
依存関係にしたくない場合は、(インストール後のサブシェルを使用して)demisxの回答を確認してください。
使用例2:ルートディレクトリからすべてのnpmコマンドを実行する場合(たとえば、サブディレクトリでnpmスクリプトを使用しない場合)、依存関係のように各サブディレクトリをインストールするだけで済みます。
npm install path/to/any/directory/with/a/package-json
後者の場合、サブディレクトリにファイルnode_modules
やpackage-lock.json
ファイルが見つからないことに驚かないでください-すべてのパッケージがルートにインストールされますnode_modules
に、npmコマンドを実行できません(依存関係が必要です)、任意のサブディレクトリから。
不明な場合は、ユースケース1が常に機能します。
run-p
必須ではありませんが、その場合はより冗長になります"postinstall": "npm run install:a && npm run install:b"
&&
なしで使用できますrun-p
。しかし、あなたが言うように、それは読みにくくなります。もう1つの欠点(インストールが並行して実行されるためrun-pが解決する)は、1つが失敗しても、他のスクリプトは影響を受けないことです
Windowsサポートをsnozzaの回答に追加しnode_modules
、存在する場合はフォルダをスキップします。
var fs = require('fs')
var resolve = require('path').resolve
var join = require('path').join
var cp = require('child_process')
// get library path
var lib = resolve(__dirname, '../lib/')
fs.readdirSync(lib)
.forEach(function (mod) {
var modPath = join(lib, mod)
// ensure path has package.json
if (!mod === 'node_modules' && !fs.existsSync(join(modPath, 'package.json'))) return
// Determine OS and set command accordingly
const cmd = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
// install folder
cp.spawn(cmd, ['i'], { env: process.env, cwd: modPath, stdio: 'inherit' })
})
ここで提供されるスクリプトに触発されて、私は構成可能な例を作成しました。
yarn
かnpm
yarn
が、ディレクトリにのみあるpackage-lock.json
場合npm
、そのディレクトリに使用されます(デフォルトはtrue)。cp.spawn
yarn workspaces
(構成可能)のconst path = require('path');
const { promises: fs } = require('fs');
const cp = require('child_process');
// if you want to have it automatically run based upon
// process.cwd()
const AUTO_RUN = Boolean(process.env.RI_AUTO_RUN);
/**
* Creates a config object from environment variables which can then be
* overriden if executing via its exported function (config as second arg)
*/
const getConfig = (config = {}) => ({
// we want to use yarn by default but RI_USE_YARN=false will
// use npm instead
useYarn: process.env.RI_USE_YARN !== 'false',
// should we handle yarn workspaces? if this is true (default)
// then we will stop recursing if a package.json has the "workspaces"
// property and we will allow `yarn` to do its thing.
yarnWorkspaces: process.env.RI_YARN_WORKSPACES !== 'false',
// if truthy, will run extra checks to see if there is a package-lock.json
// or yarn.lock file in a given directory and use that installer if so.
detectLockFiles: process.env.RI_DETECT_LOCK_FILES !== 'false',
// what kind of logging should be done on the spawned processes?
// if this exists and it is not errors it will log everything
// otherwise it will only log stderr and spawn errors
log: process.env.RI_LOG || 'errors',
// max depth to recurse?
maxDepth: process.env.RI_MAX_DEPTH || Infinity,
// do not install at the root directory?
ignoreRoot: Boolean(process.env.RI_IGNORE_ROOT),
// an array (or comma separated string for env var) of directories
// to skip while recursing. if array, can pass functions which
// return a boolean after receiving the dir path and fs.Dirent args
// @see https://nodejs.org/api/fs.html#fs_class_fs_dirent
skipDirectories: process.env.RI_SKIP_DIRS
? process.env.RI_SKIP_DIRS.split(',').map(str => str.trim())
: undefined,
// just run through and log the actions that would be taken?
dry: Boolean(process.env.RI_DRY_RUN),
...config
});
function handleSpawnedProcess(dir, log, proc) {
return new Promise((resolve, reject) => {
proc.on('error', error => {
console.log(`
----------------
[RI] | [ERROR] | Failed to Spawn Process
- Path: ${dir}
- Reason: ${error.message}
----------------
`);
reject(error);
});
if (log) {
proc.stderr.on('data', data => {
console.error(`[RI] | [${dir}] | ${data}`);
});
}
if (log && log !== 'errors') {
proc.stdout.on('data', data => {
console.log(`[RI] | [${dir}] | ${data}`);
});
}
proc.on('close', code => {
if (log && log !== 'errors') {
console.log(`
----------------
[RI] | [COMPLETE] | Spawned Process Closed
- Path: ${dir}
- Code: ${code}
----------------
`);
}
if (code === 0) {
resolve();
} else {
reject(
new Error(
`[RI] | [ERROR] | [${dir}] | failed to install with exit code ${code}`
)
);
}
});
});
}
async function recurseDirectory(rootDir, config) {
const {
useYarn,
yarnWorkspaces,
detectLockFiles,
log,
maxDepth,
ignoreRoot,
skipDirectories,
dry
} = config;
const installPromises = [];
function install(cmd, folder, relativeDir) {
const proc = cp.spawn(cmd, ['install'], {
cwd: folder,
env: process.env
});
installPromises.push(handleSpawnedProcess(relativeDir, log, proc));
}
function shouldSkipFile(filePath, file) {
if (!file.isDirectory() || file.name === 'node_modules') {
return true;
}
if (!skipDirectories) {
return false;
}
return skipDirectories.some(check =>
typeof check === 'function' ? check(filePath, file) : check === file.name
);
}
async function getInstallCommand(folder) {
let cmd = useYarn ? 'yarn' : 'npm';
if (detectLockFiles) {
const [hasYarnLock, hasPackageLock] = await Promise.all([
fs
.readFile(path.join(folder, 'yarn.lock'))
.then(() => true)
.catch(() => false),
fs
.readFile(path.join(folder, 'package-lock.json'))
.then(() => true)
.catch(() => false)
]);
if (cmd === 'yarn' && !hasYarnLock && hasPackageLock) {
cmd = 'npm';
} else if (cmd === 'npm' && !hasPackageLock && hasYarnLock) {
cmd = 'yarn';
}
}
return cmd;
}
async function installRecursively(folder, depth = 0) {
if (dry || (log && log !== 'errors')) {
console.log('[RI] | Check Directory --> ', folder);
}
let pkg;
if (folder !== rootDir || !ignoreRoot) {
try {
// Check if package.json exists, if it doesnt this will error and move on
pkg = JSON.parse(await fs.readFile(path.join(folder, 'package.json')));
// get the command that we should use. if lock checking is enabled it will
// also determine what installer to use based on the available lock files
const cmd = await getInstallCommand(folder);
const relativeDir = `${path.basename(rootDir)} -> ./${path.relative(
rootDir,
folder
)}`;
if (dry || (log && log !== 'errors')) {
console.log(
`[RI] | Performing (${cmd} install) at path "${relativeDir}"`
);
}
if (!dry) {
install(cmd, folder, relativeDir);
}
} catch {
// do nothing when error caught as it simply indicates package.json likely doesnt
// exist.
}
}
if (
depth >= maxDepth ||
(pkg && useYarn && yarnWorkspaces && pkg.workspaces)
) {
// if we have reached maxDepth or if our package.json in the current directory
// contains yarn workspaces then we use yarn for installing then this is the last
// directory we will attempt to install.
return;
}
const files = await fs.readdir(folder, { withFileTypes: true });
return Promise.all(
files.map(file => {
const filePath = path.join(folder, file.name);
return shouldSkipFile(filePath, file)
? undefined
: installRecursively(filePath, depth + 1);
})
);
}
await installRecursively(rootDir);
await Promise.all(installPromises);
}
async function startRecursiveInstall(directories, _config) {
const config = getConfig(_config);
const promise = Array.isArray(directories)
? Promise.all(directories.map(rootDir => recurseDirectory(rootDir, config)))
: recurseDirectory(directories, config);
await promise;
}
if (AUTO_RUN) {
startRecursiveInstall(process.cwd());
}
module.exports = startRecursiveInstall;
そしてそれが使用されています:
const installRecursively = require('./recursive-install');
installRecursively(process.cwd(), { dry: true })
find
システムにユーティリティがある場合は、アプリケーションのルートディレクトリで次のコマンドを実行してみてください。
find . ! -path "*/node_modules/*" -name "package.json" -execdir npm install \;
基本的に、すべてのpackage.json
ファイルを見つけnpm install
てそのディレクトリで実行し、すべてのnode_modules
ディレクトリをスキップします。
find . ! -path "*/node_modules/*" ! -path "*/additional_path/*" -name "package.json" -execdir npm install \;