এখানে সরবরাহিত স্ক্রিপ্টগুলি দ্বারা অনুপ্রাণিত হয়ে আমি একটি কনফিগারযোগ্য উদাহরণ তৈরি করেছি যা:
- ব্যবহার করতে সেটআপ করা যেতে পারে
yarn
বাnpm
- লক ফাইলের উপর ভিত্তি করে কমান্ডটি নির্ধারণের জন্য সেটআপ করা যেতে পারে যাতে আপনি এটি ব্যবহার করতে সেট করেন
yarn
তবে একটি ডিরেক্টরিতে কেবল এটি থাকে সেই ডিরেক্টরিতে package-lock.json
এটি ব্যবহার করা হবে npm
(সত্য সত্যই ডিফল্ট)।
- লগিং কনফিগার করুন
- সমান্তরাল ব্যবহার করে ইনস্টলেশন চালায়
cp.spawn
- এটি প্রথমে কী করবে তা আপনাকে দেখতে শুকনো রান করতে পারে
- env vars ব্যবহার করে একটি ফাংশন বা অটো চালানো হিসাবে চালানো যেতে পারে
- যখন একটি ফাংশন হিসাবে চালানো হয়, চেক করার জন্য directoriesচ্ছিকভাবে ডিরেক্টরিগুলির অ্যারে সরবরাহ করুন
- সম্পন্ন হলে সমাধান হয় এমন প্রতিশ্রুতি দেয়
- প্রয়োজনে সর্বাধিক গভীরতা নির্ধারণ করার অনুমতি দেয়
yarn workspaces
(কনফিগারযোগ্য) সহ কোনও ফোল্ডার পেলে পুনরাবৃত্তি বন্ধ করতে জানে
- কমা দ্বারা বিচ্ছিন্ন এনভ ভার ব্যবহার করে বা কনফিগার দিয়ে স্ট্রিংগুলির একটি অ্যারের সাথে মেলে এমন ফাইলের নাম, ফাইলের পাথ এবং fs.Dirent আপত্তি প্রাপ্ত করে এবং বুলিয়ান ফলাফল প্রত্যাশা করে ডিরেক্টরিগুলি এড়িয়ে যাওয়ার অনুমতি দেয়।
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 })