programing

중첩된 폴더에 대해 npm 설치를 실행하는 가장 좋은 방법은 무엇입니까?

testmans 2023. 5. 15. 21:23
반응형

중첩된 폴더에 대해 npm 설치를 실행하는 가장 좋은 방법은 무엇입니까?

가장 올바른 설치 방법은 무엇입니까?npm packages중첩된 하위 폴더에 있습니까?

my-app
  /my-sub-module
  package.json
package.json

가장 좋은 방법은 무엇입니까?packages/my-sub-module다음 시간에 자동으로 설치됩니다.npm install에 달려들다my-app?

중첩된 하위 디렉터리의 이름을 알고 있다면 설치 후 사용을 선호합니다.package.json:

"scripts": {
  "postinstall": "cd nested_dir && npm install",
  ...
}

@Scott의 대답에 따르면 하위 디렉터리 이름을 알고 있는 한 install|postinstall 스크립트가 가장 간단한 방법입니다.이것이 제가 여러 하위 디렉터리에 대해 실행하는 방법입니다.예를 들어, 우리가 가지고 있다고 가정해 보세요.api/,web/그리고.shared/모노레포 루트의 하위 접두사:

// In monorepo root package.json
{
...
 "scripts": {
    "postinstall": "(cd api && npm install); (cd web && npm install); (cd shared && npm install)"
  },
}

윈도우즈의 윈도우즈의 합니다.; 모사 에이의에▁the로 괄호 .&&.

// In monorepo root package.json
{
...
 "scripts": {
    "postinstall": "(cd api && npm install) && (cd web && npm install) && (cd shared && npm install)"
  },
}

사용 사례 1: 각 하위 디렉터리(각 패키지) 내에서 npm 명령을 실행할 수 있는 경우.json is)를 사용해야 합니다.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이 항상 작동합니다.

를 중첩된 폴더에 에는 npm 패키지를 통해 할 수 .npm 메인 및본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에서 작동하도록 만들었습니다.

사람들이 이 질문을 마주칠 경우를 대비해서 참고하기 위해서입니다.이제 할 수 있는 일:

  • 패키지를 추가합니다.json을 하위 폴더로
  • 이 하위 폴더를 기본 패키지에 참조 링크로 설치합니다.json:

npm install --save path/to/my/subfolder

저의 해결책은 매우 비슷합니다.순수 노드.js

는 모든 가 "" "" "" "" "" "" "" "" "" ""만 있으면 모든 하위 폴더를 으로) 합니다.package.json그리고 실행npm install그들 각각에서.할 수 . 에는 "" " " " "가되지 않습니다. 폴더는 허용되지 않습니다.package.json아래의 예에서 이러한 폴더는 "패키지"입니다.이 스크립트는 "사전 설치" 스크립트로 실행할 수 있습니다.

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))
}

하지만, 은 승된답작다사수용있다습니할음을을 사용할 수 .--prefix선택한 위치에서 npm 명령을 실행합니다.

"postinstall": "npm --prefix ./nested_dir install"

그리고.--prefix "npm 에▁just"가 아닌 모든 명령에 합니다.install.

또한 현재 접두사를 볼 수 있습니다.

npm prefix

다음을 사용하여 글로벌 설치(-g) 폴더를 설정합니다.

npm config set prefix "folder_path"

TMI일 수도 있지만, 생각해보면 알겠지만,

가지고 계신다면,find유틸리티를 사용하면 응용 프로그램 루트 디렉터리에서 다음 명령을 실행할 수 있습니다.
find . ! -path "*/node_modules/*" -name "package.json" -execdir npm install \;

package.json 및 파일실행npm install디렉터리에서 ▁all▁skipping를 건너뛰는node_modules디렉터리.

편집 fgblomqvist가 코멘트에서 언급했듯이, npm은 이제 워크스페이스도 지원합니다.


몇몇 대답들은 꽤 오래되었습니다.는 요즘 모노레포스를 설정할 수 있는 새로운 옵션이 있다고 생각합니다.

  1. 작업 공간을 사용하는 것이 좋습니다.

워크스페이스는 Yarn 1.0부터 기본적으로 사용할 수 있는 패키지 아키텍처를 설정하는 새로운 방법입니다.실행하기만 하면 되는 방식으로 여러 패키지를 설정할 수 있습니다.yarn install한 번에 모든 항목을 단일 경로로 설치합니다.

  1. 만약 당신이 npm과 함께 있는 것을 좋아하거나 해야 한다면, 나는 lerna를 보는 것을 제안합니다.

Lerna는 git 및 npm을 사용하여 다중 패키지 저장소 관리와 관련된 워크플로우를 최적화하는 도구입니다.

Lerna는 실 작업 공간에서도 완벽하게 작동합니다 - 기사.모노레포 프로젝트 설정을 방금 마쳤습니다. 를 들어 보겠습니다.

그리고 다음은 npm + lerna - MDC Web을 사용하도록 구성된 다중 패키지 프로젝트의 예입니다: 그들은 실행됩니다.lerna bootstrap패키지 사용.제이슨의postinstall.

Snozza의 답변에 Windows 지원 추가 및 다음 작업 건너뛰기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해당 디렉터리에 대해(참으로 표시됨).
  • 로깅 구성
  • 합니다.cp.spawn
  • 먼저 무엇을 하는지 볼 수 있도록 모의 실행을 수행할 수 있습니다.
  • 변수를 사용하여 함수로 실행하거나 자동 실행할 수 있습니다.
    • 함수로 실행될 때, 선택적으로 확인할 디렉터리 배열을 제공합니다.
  • 완료되면 확인되는 약속을 반환합니다.
  • 필요한 경우 최대 깊이를 확인하도록 설정할 수 있습니다.
  • 는 의폴찾경재우중귀지함있알다습니고을야해를을이 있는 를 중지하도록 있습니다.yarn workspaces 가능설정 가능)
  • 에서는 쉼표로 구분된 환경 변수를 사용하거나 일치시킬 문자열 배열 구성이나 파일 이름, 파일 경로 및 fs를 수신하는 함수를 전달하여 디렉터리를 건너뛸 수 있습니다.다른 obj이며 부울 결과를 예상합니다.
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 . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && npm install" \;

[macOS, Linux 사용자의 경우]:

프로젝트 및 중첩 폴더에 모든 종속성을 설치하기 위해 bash 파일을 만들었습니다.

find . -name node_modules -prune -o -name package.json -execdir npm install \;

설명:루트 디렉토리에서 다음을 제외합니다.node_modules폴더 에서도), "" "" "" "" "" ""가 있는 .package.json파일을 작성한 다음 실행합니다.npm install지휘권

지정된 폴더(예: abc123, def456 폴더)에서 찾으려는 경우 다음과 같이 실행합니다.

find ./abc123/* ./def456/* -name node_modules -prune -o -name package.json -execdir npm install \;

실행하기npm install모든 하위 디렉터리에서 다음과 같은 작업을 수행할 수 있습니다.

"scripts": {
  ...
  "install:all": "for D in */; do npm install --cwd \"${D}\"; done"
}

어디에

install:all입니다. 할 수 있습니다. 원하는 이름을 지정할 수 있습니다.

D입니다.

*/하위 디렉터리를 찾을 위치를 지정합니다. directory/*/에는 내의모디나다열에 있는 모든 됩니다.directory/그리고.directory/*/*/모든 디렉토리가 두 단계로 나열됩니다.

npm install -cwd합니다.

다음과 같은 여러 명령을 실행할 수도 있습니다.

for D in */; do echo \"Installing stuff on ${D}\" && npm install --cwd \"${D}\"; done

반복할 때마다 "_subfolder/에 항목 설치"가 인쇄됩니다.

이 기능은 다음과 같습니다.yarn

디렉토리 목록을 가져오고 셸 명령을 실행할 수 있는 모든 언어를 사용할 수 있습니다.

OP가 정확히 의도한 답이 아니라는 것을 알지만, 그것은 항상 작동할 것입니다. "" " " " 하위디리 이배만다해합루이실다니행야해여프하름을당음든렉터름열의을▁you합다니▁namesdirectory,▁run▁create▁need▁of실야▁to행해▁an하위▁over▁array▁sub▁then▁them루"npm i또는 실행해야 하는 명령어가 무엇이든 간에.

참고로, 저는 노력했습니다.npm i **/부모의 모든 하위 디렉터리에서 모듈을 설치했습니다.그것은 전혀 직관적이지 않지만, 말할 필요도 없이 당신이 필요로 하는 해결책이 아닙니다.

언급URL : https://stackoverflow.com/questions/31773546/the-best-way-to-run-npm-install-for-nested-folders

반응형