Part 1-How to install npm packages programmatically?
In this article, we review how Father, an NPM package development tool is installs npm dependencies programmatically.
How did I find this code snippet?
I wrote an article about Father and in its README.md, I found that it supports micro generators that adds commonly used engineering capabilities to projects, such as setting up Jest for testing. This is similar to the CLI tool I am building, to generate code for authentication or S3 upload in Next.js.
I started searching for the micro generators code and found a folder named commands/generators.
Common pattern in the generators
You will see that there is a common pattern in the way these generators are defined.
generators/eslint.ts
eslint.ts has the below code
import { GeneratorType } from '@umijs/core';
import { logger } from '../../utils';
import fg from 'fast-glob';
import { writeFileSync } from 'fs';
import { join } from 'path';
import { IApi } from '../../types';
import { GeneratorHelper } from './utils';
export default (api: IApi) => {
api.describe({
key: 'generator:eslint',
});
api.registerGenerator({
key: 'eslint',
name: 'Enable ESLint',
description: 'Setup ESLint Configuration',
type: GeneratorType.enable,
checkEnable: () => {
...
},
disabledDescription:
'ESLint has already enabled. You can remove .eslintrc, then run this again to re-setup.',
fn: async () => {
....
},
});
};
generators/jest.ts
jest.ts has the below definition:
import { GeneratorType } from '@umijs/core';
import { logger } from '../../utils';
import { existsSync, writeFileSync } from 'fs';
import { join } from 'path';
import { IApi } from '../../types';
import { GeneratorHelper, promptsExitWhenCancel } from './utils';
export default (api: IApi) => {
api.describe({
key: 'generator:jest',
});
api.registerGenerator({
key: 'jest',
name: 'Enable Jest',
description: 'Setup Jest Configuration',
type: GeneratorType.enable,
checkEnable: () => {
...
},
disabledDescription:
'Jest has already enabled. You can remove jest.config.{ts,js}, then run this again to re-setup.',
fn: async () => {
...
},
});
};
Do you see the common pattern in these two definitions above? there is
- api.describe
This accepts an object that has the below properties:
- key
- api.registerGenerator
This accepts an object that has the below properties:
-
key
-
name
-
description
-
type
-
checkEnable
-
disabledDescription
-
fn
h.installDeps()
At line 97 in generators/jest.ts, you find this below code snippet
h.installDeps();
What is h here? At line 27, you will see h is initialized as shown below:
const h = new GeneratorHelper(api);
To see how the installDeps is defined, we need to review GeneratorHelper
GeneratorHelper
GeneratorHelper has the below shown functions at the time of writing this article
import { getNpmClient, installWithNpmClient, prompts } from '@umijs/utils';
import { writeFileSync } from 'fs';
import { IApi } from '../../types';
import { logger } from '../../utils';
export class GeneratorHelper {
constructor(readonly api: IApi) {}
addDevDeps(deps: Record<string, string>) {
...
}
addScript(name: string, cmd: string) {
...
}
private addScriptToPkg(name: string, cmd: string) {
...
}
installDeps() {
...
}
}
installDeps
installDeps is defined as shown below in GeneratorHelper.
installDeps() {
const { api } = this;
const npmClient = getNpmClient({ cwd: api.cwd });
installWithNpmClient({
npmClient,
});
logger.quietExpect.info(`Install dependencies with ${npmClient}`);
}
There are two functions that we need to learn about to understand how Father installs npm deps programmatically.
-
getNpmClient
-
installWithNpmClient
This will be discussed in part 2, i.e., in the next article.
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