In the previous article, we reviewed some code in Father, an NPM package development tool, related to installing npm packages programmatically and this led us to discover two functions in the installDeps.
installDeps() {
const { api } = this;
const npmClient = getNpmClient({ cwd: api.cwd });
installWithNpmClient({
npmClient,
});
logger.quietExpect.info(`Install dependencies with ${npmClient}`);
}
We will review the two functions listed below:
getNpmClient
installWithNpmClient
getNpmClient
getNpmClient is imported as shown below
import { getNpmClient, installWithNpmClient, prompts } from '@umijs/utils';
I did search for utils repo in the umijs organisation.
Since Father codebase is a not a monorepo, @umijs/utils has to be an npm package. You will find this utils package in umi repository.
Finally, getNpmClient code can be found in utils/npmClient.ts
export const getNpmClient = (opts: { cwd: string }): NpmClient => {
const tnpmRegistries = ['.alibaba-inc.', '.antgroup-inc.'];
const tcnpmLockPath = join(opts.cwd, 'node_modules', '.package-lock.json');
const chokidarPkg = require('chokidar/package.json');
// detect tnpm/cnpm client
// all situations:
// - npminstall mode + native fs => generate _resolved field in package.json
// - npminstall mode + rapid fs => generate .package-lock.json in node_modules
// - npm mode + native fs => generate .package-lock.json in node_modules
// - npm mode + rapid fs => generate .package-lock.json in node_modules
// all conditions:
// - has _resolved field or .package-lock.json means tnpm/cnpm
// - _resolved field or .package-lock.json contains tnpm registry means tnpm
if (chokidarPkg._resolved) {
return tnpmRegistries.some((r) => chokidarPkg._resolved.includes(r))
? 'tnpm'
: 'cnpm';
} else if (existsSync(tcnpmLockPath)) {
const tcnpmLock = readFileSync(tcnpmLockPath, 'utf-8');
return tnpmRegistries.some((r) => tcnpmLock.includes(r)) ? 'tnpm' : 'cnpm';
}
const chokidarPath = require.resolve('chokidar');
if (
chokidarPath.includes('.pnpm') ||
existsSync(join(opts.cwd, 'node_modules', '.pnpm'))
) {
return 'pnpm';
}
if (
existsSync(join(opts.cwd, 'yarn.lock')) ||
existsSync(join(opts.cwd, 'node_modules', '.yarn-integrity'))
) {
return 'yarn';
}
return 'npm';
};
Detect tnpm/cnpm client
has _resolved field or .package-loc.json means tnpm/cnpm
This comment explains what this below if block is about
if (chokidarPkg._resolved) {
return tnpmRegistries.some((r) => chokidarPkg._resolved.includes(r))
? 'tnpm'
: 'cnpm';
}
- _resolved field or .package-lock.json contains tnpm registry means tnpm
This comment above explains the else if block below
else if (existsSync(tcnpmLockPath)) {
const tcnpmLock = readFileSync(tcnpmLockPath, 'utf-8');
return tnpmRegistries.some((r) => tcnpmLock.includes(r)) ? 'tnpm' : 'cnpm';
}
This is the first time I am learning about tnpm or cnpm. I will write an article about these.
Detect pnpm client
const chokidarPath = require.resolve('chokidar');
if (
chokidarPath.includes('.pnpm') ||
existsSync(join(opts.cwd, 'node_modules', '.pnpm'))
) {
return 'pnpm';
}
Detect yarn client
if (
existsSync(join(opts.cwd, 'yarn.lock')) ||
existsSync(join(opts.cwd, 'node_modules', '.yarn-integrity'))
) {
return 'yarn';
}
if none of the above conditions are met, this function defaults to “npm”.
installWithNpmClient
In the same packages/utils/npmClient.ts fiile, you will find the installWithNpmClient.
export const installWithNpmClient = ({
npmClient,
cwd,
}: {
npmClient: NpmClient;
cwd?: string;
}): void => {
const { sync } = require('../compiled/cross-spawn');
// pnpm install will not install devDependencies when NODE_ENV === 'production'
// we should remove NODE_ENV to make sure devDependencies can be installed
const { NODE_ENV: _, ...env } = process.env;
const npm = sync(npmClient, [npmClient === 'yarn' ? '' : 'install'], {
stdio: 'inherit',
cwd,
env,
});
if (npm.error) {
throw npm.error;
}
};
We need to learn more about sync function. sync is required from compiled/cross-spawn. Uncompiled cross-spawn can be found at node-cross-spawn. How do I know? both of these have the common name mentioned — André Cruz in utils/compiled/cross-spawn/package.json and in node-cross-spawn package.json.
Seeing this “compiled” rings a bell for me. I have seen similar compiled folder in Next.js source code.
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on interesting projects. Send me an email at ramu.narasinga@gmail.com
My Github — https://github.com/ramu-narasinga
My website — https://ramunarasinga.com
My Youtube channel — https://www.youtube.com/@thinkthroo
Learning platform — https://thinkthroo.com
Codebase Architecture — https://app.thinkthroo.com/architecture
Best practices — https://app.thinkthroo.com/best-practices
Production-grade projects — https://app.thinkthroo.com/production-grade-projects
Top comments (0)