1. Introduction
Hi, I’m Wakawa. Welcome to follow my official account Ruochuan Vision. Recently, I organized an activity to read the source code together, and those who are interested can join in with my wechat account RuoChuan12. It has been going on for more than two months.
If you want to learn source code, Highly recommend before I wrote “learning source code overall architecture series” Includes jQuery, underscore, Lodash, vuex, Sentry, AXIos, Redux, KOA, vue-devtools, vuex4, koa-compose, VUe-next-release, vue-this, creation-V Ue, toy vite, more than 10 source code articles.
This article warehouse ni-analysis, ask a star^_^
Recently, we organized a source code reading activity to learn source code together. So we search for all kinds of worth learning, and the code line is not many source.
I wrote two previous articles about Vue3.
- Beginners can also understand Vue3 source code in those practical basic tool functions
- Vue 3.2 has been released, so how did Yu Creek release vue.js?
The article is all about using YARN. Yarn Install cannot install dependencies. Yarn Install cannot install dependencies. Yarn Install cannot install dependencies. So I went to the Github warehouse to have a look and found that Yu Yu Creek had changed the Vue3 warehouse from YARN to PNPM. There is a sentence in the contribution document.
We also recommend installing ni to help switching between repos using different package managers.
ni
also provides the handynr
command which running npm scripts easier.
We also recommend installing NI to help switch between Repos using different package managers. Ni also provides convenient NR commands that make it easier to run NPM scripts.
This NI project source code although is TS, has not used TS partners is also very good understanding, and the main file is actually less than 100 lines, very suitable for us to learn.
Reading this article, you will learn:
1. Learn to use ni and understand its principle 2. Learn to debug and learn source code 3. You can also use NI 4 in your daily work, etcCopy the code
2. Use
Look at the NI Github documentation.
npm i in a yarn project, again? F**k!ni – use the right package manager
Global installation.
npm i -g @antfu/ni
Copy the code
If the global installation encounters a conflict, we can force the installation by adding the –force parameter.
I look at the source found: the following commands, can be appended to the end \? Is printed, but not executed.
Here are a few common examples.
So after the global installation, you can test as much as you like, such as ni \? Nr dev –port=3000 \? The nx jest \? , because print, so can be executed in a variety of directories, help to understand ni source.
Ni – 2.1 install
ni
# npm install
# yarn install
# pnpm install
Copy the code
ni axios
# npm i axios
# yarn add axios
# pnpm i axios
Copy the code
2.2 nr – run
nr dev --port=3000
# npm run dev -- --port=3000
# yarn run dev --port=3000
# pnpm run dev -- --port=3000
Copy the code
nr
# Interactive select command to execute
# interactively select the script to run
# supports https://www.npmjs.com/package/npm-scripts-info convention
Copy the code
nr -
Run the last command
# rerun the last command
Copy the code
2.3 nx – execute
nx jest
# npx jest
# yarn dlx jest
# pnpm dlx jest
Copy the code
Principle 3.
Making warehouse ni# how
Ni assumes you use lock files (and you should)
Before it runs, it checks your yarn.lock/pnpm-lock.yaml/package-lock.json to know the current package manager and runs the corresponding command.
From this statement alone, we can see that this tool must do three things:
1. Guess which package manager to use according to the lock file: NPM/YARN/PNPM 2. Smooth out the command differences between different package managers. 3. Run the corresponding scriptsCopy the code
4. Preparation before reading the source code
4.1 the cloning
# Recommend cloning my repository (my guarantee corresponds to the version of this article)
git clone https://github.com/lxchuan12/ni-analysis.git
cd ni-analysis/ni
# npm i -g pnpm
# install dependencies
pnpm i
You can also use ni
# or clone the official repository
git clone https://github.com/vuejs/ni.git
cd ni
# npm i -g pnpm
# install dependencies
pnpm i
You can also use ni
Copy the code
As you know, when looking at an open source project, start with a package.json file.
4.2 package. Json file
{
"name": "@antfu/ni"."version": "0.10.0"."description": "Use the right package manager".// Six commands are exposed
"bin": {
"ni": "bin/ni.js"."nci": "bin/nci.js"."nr": "bin/nr.js"."nu": "bin/nu.js"."nx": "bin/nx.js"."nrm": "bin/nrm.js"
},
"scripts": {
// Omit other commands to execute ts files with esno
// Can be added? Easy to debug, you can not add
NPM run dev \?
"dev": "esno src/ni.ts ?"}},Copy the code
Following the dev command, we find the main entry file SRC /ni.ts.
4.3 Start debugging from the main source code entry
// ni/src/ni.ts
import { parseNi } from './commands'
import { runCli } from './runner'
// We can break here
runCli(parseNi)
Copy the code
Locate the scripts for ni/package.json and hover the mouse over the dev command to bring up the run script and debug script commands. Select the debug script as shown below.
5. Main flow runner – runCli function
This function parses the command-line arguments passed by the terminal. Ultimately, the run function is executed.
For readers who are not familiar with process, you can look at the process object written by Teacher Ruan Yifeng
// ni/src/runner.ts
export async function runCli(fn: Runner, options: DetectOptions = {}) {
Process. argv: Returns an array of all the command-line arguments of the current process.
The first and second elements of process.argv are the fully qualified file system paths for the Node executable and the JavaScript files being executed, whether or not you enter them as such.
const args = process.argv.slice(2).filter(Boolean)
try {
await run(fn, args, options)
}
catch (error) {
The process.exit method is used to exit the current process. It can take a numeric argument, and if the argument is greater than 0, the execution fails. If the value is 0, the execution succeeds.
process.exit(1)}}Copy the code
Let’s move on to the run function.
6. Main process runner – run main function
This function does three main things:
1. Guess which package manager NPM/YARN/pnpm-detect function to use based on the lock file. 2. Smooth command differences between different package managers - parseNi function 3. Finally run the corresponding script - execa toolCopy the code
// ni/src/runner.ts
// The source code has been deleted
import execa from 'execa'
const DEBUG_SIGN = '? '
export async function run(fn: Runner, args: string[], options: DetectOptions = {}) {
// The command argument contains the question mark? Is in debug mode and does not execute scripts
const debug = args.includes(DEBUG_SIGN)
if (debug)
// In debug mode, delete the question mark
remove(args, DEBUG_SIGN)
// the CWD method returns the current directory of the process (absolute path)
let cwd = process.cwd()
let command
// You can specify a file directory
// ni -C packages/foo vite
// nr -C playground dev
if (args[0= = ='-C') {
cwd = resolve(cwd, args[1])
-c packages/foo -c packages/foo
args.splice(0.2)}// If the installation is global, use the global package manager
const isGlobal = args.includes('-g')
if (isGlobal) {
command = await fn(getGlobalAgent(), args)
}
else {
let agent = awaitdetect({ ... options, cwd }) || getDefaultAgent()// Guess which package manager to use. If no lock file is found, null is returned, and getDefaultAgent is called
if (agent === 'prompt') {
agent = (await prompts({
name: 'agent'.type: 'select'.message: 'Choose the agent'.choices: agents.map(value= > ({ title: value, value })),
})).agent
if(! agent)return
}
// fn is the function passing in the parsing code
command = await fn(agent as Agent, args, {
hasLock: Boolean(agent),
cwd,
})
}
// If there is no command, the last runCli function returns an error and exits the process
if(! command)return
// In debug mode, print the command directly. Debugging is very useful.
if (debug) {
// eslint-disable-next-line no-console
console.log(command)
return
}
// Finally use execA to execute commands, such as NPM I
// https://github.com/sindresorhus/execa
// Introduction: Process execution for humans
await execa.command(command, { stdio: 'inherit'.encoding: 'utf-8', cwd })
}
Copy the code
With the main process behind us, we move on to two important functions: the detect function and the parseNi function.
We can tell by the entrance.
RunCli (parseNi) run(fn) Where fn is parseNiCopy the code
6.1 Guess which package manager (NPM/YARN/PNPM) to use based on the lock file – detect function
There’s relatively little code, so I put it all out there.
1. Find the lock file in the root path of the project. Return the corresponding package manager 'NPM/YARN/PNPM'. 2. If not found, return 'null'. 3. If the command is found but the computer does not have the command, the system asks the user whether the command is automatically installed.Copy the code
// ni/src/agents.ts
export const LOCKS: Record<string, Agent> = {
'pnpm-lock.yaml': 'pnpm'.'yarn.lock': 'yarn'.'package-lock.json': 'npm',}Copy the code
// ni/src/detect.ts
export async function detect({ autoInstall, cwd }: DetectOptions) {
const result = await findUp(Object.keys(LOCKS), { cwd })
const agent = (result ? LOCKS[path.basename(result)] : null)
if(agent && ! cmdExists(agent)) {if(! autoInstall) {console.warn(`Detected ${agent} but it doesn't seem to be installed.\n`)
if (process.env.CI)
process.exit(1)
const link = terminalLink(agent, INSTALL_PAGE[agent])
const { tryInstall } = await prompts({
name: 'tryInstall'.type: 'confirm'.message: `Would you like to globally install ${link}? `,})if(! tryInstall) process.exit(1)}await execa.command(`npm i -g ${agent}`, { stdio: 'inherit', cwd })
}
return agent
}
Copy the code
Now let’s look at the parseNi function.
6.2 Smoothing command differences between different package managers – parseNi function
// ni/src/commands.ts
export const parseNi = <Runner>((agent, args, ctx) = > {
// ni -v outputs the version number
if (args.length === 1 && args[0= = ='-v') {
// eslint-disable-next-line no-console
console.log(`@antfu/ni v${version}`)
process.exit(0)}if (args.length === 0)
return getCommand(agent, 'install')
// Omit some code
})
Copy the code
Get the command using getCommand.
// ni/src/agents.ts
/ / a cut
// a configuration to write the commands in the three package managers.
export const AGENTS = {
npm: {
'install': 'npm i'
},
yarn: {
'install': 'yarn install'
},
pnpm: {
'install': 'pnpm i'}},Copy the code
// ni/src/commands.ts
export function getCommand(
agent: Agent,
command: Command,
args: string[] = [],
) {
// Error if package manager is not in AGENTS
// For example, NPM is not present
if(! (agentin AGENTS))
throw new Error(`Unsupported agent "${agent}"`)
// NPM install for command installation
const c = AGENTS[agent][command]
// If it is a function, the function is executed.
if (typeof c === 'function')
return c(args)
// If the command is not found, an error is reported
if(! c)throw new Error(`Command "${command}" is not support by agent "${agent}"`)
// Finally concatenate the command string
return c.replace('{0}', args.join(' ')).trim()
}
Copy the code
6.3 Run the corresponding script
Get the corresponding command, such as NPM I, and finally use the tool execa to execute the resulting corresponding script.
await execa.command(command, { stdio: 'inherit'.encoding: 'utf-8', cwd })
Copy the code
7. To summarize
After we read the source code, we can know that this artifact NI mainly does three things:
1. Guess which package manager NPM/YARN/pnpm-detect function to use based on the lock file. 2. Smooth command differences between different package managers - parseNi function 3. Finally run the corresponding script - execa toolCopy the code
In our daily development, it may be easy to use NPM, YARN, and PNPM together. Once you have NI, you can use it for everyday development. Vue core member Anthony Fu discovered the problem and eventually developed a tool ni to solve it. The ability to find and solve problems is what front-end development engineers need.
In addition, I found that much of the Vue ecosystem was basically switched to using PNPM.
Because the article should not be too long, so did not fully expand on all the details of the source code. It is highly recommended that readers use VSCode to debug NI source code in accordance with the method in the article. After learning to debug source code, source code is not as difficult as imagined.
Finally, you can continue to pay attention to me @Ruogawa. Welcome to join us in ruochuan12 and learn about the source code together.