NPM
Mirrors
npm config set disturl https://npmmirror.com/mirrors/node/
npm config set chromedriver_cdnurl https://npmmirror.com/mirrors/chromedriver/
npm config set electron_mirror https://npmmirror.com/mirrors/electron/
npm config set electron_builder_binaries_mirror https://npmmirror.com/mirrors/electron-builder-binaries/
npm config set operadriver_cdnurl https://npmmirror.com/mirrors/operadriver/
npm config set phantomjs_cdnurl https://npmmirror.com/mirrors/phantomjs/
npm config set profiler_binary_host_mirror https://npmmirror.com/mirrors/node-inspector/
npm config set puppeteer_download_host https://npmmirror.com/mirrors/
npm config set python_mirror https://npmmirror.com/mirrors/python/
npm config set robotjs_binary_host https://npmmirror.com/mirrors/robotjs/
npm config set sass_binary_site https://npmmirror.com/mirrors/node-sass/
npm config set saucectl_install_binary_mirror https://npmmirror.com/mirrors/saucectl/
npm config set sentrycli_cdnurl https://npmmirror.com/mirrors/sentry-cli/
npm config set sharp_binary_host https://npmmirror.com/mirrors/sharp/
npm config set sharp_libvips_binary_host https://npmmirror.com/mirrors/sharp-libvips/
npm config set sqlite3_binary_site https://npmmirror.com/mirrors/sqlite3/
npm config set swc_binary_site https://npmmirror.com/mirrors/node-swc/
Node Version Manager
NVM
NVM:
scoop install nvm
nvm i lts
nvm use lts
node -v
FNM
FNM:
brew install fnm
scoop install fnm
winget install Schniz.fnm
echo 'eval "$(fnm env --use-on-cd --shell bash)"' >> ~/.bashrc
fnm i --lts
node -v
Tab Completion
npm completion >> ~/.bashrc (or ~/.zshrc)
source ~/.zshrc
Commands
npm adduser
mkdir proj/
# 修改 package.json 可再次运行此命令
# scope for everyone
npm init --scope=<username>
# 修改 package.json 可再次运行此命令(不接模块名为自动更新)
npm install -S <package>
npm install -D <package>
npm prune # 清除无用包
npm rm --save # --save 删除文件的同时更新 package.json 文件
npm ls
npm outdated # 去除过期包
npm ci for cache install (speed up installation):
# With package-lock.json exists:
npm ci
Remove useless package:
# Uninstall node_modules not in package.json
npm prune
npm outdated
Testing
{
"scripts": {
"test": "node test.js"
}
}
npm test
Publish
latest or alpha:
npm publish
npm publish --tag [<tag>]
npm dist-tag add <pkg>@<version> [<tag>]
npm dist-tag rm <pkg> <tag>
npm dist-tag ls [<pkg>]
NPM registry token configuration:
npm config set @orgName:registry https://registry.example.com
npm config set //registry.example.com/:_authToken XXXXXTokenXXXXX
Release script from VitePress:
const fs = require('node:fs')
const path = require('node:path')
const chalk = require('chalk')
const { prompt } = require('enquirer')
const execa = require('execa')
const semver = require('semver')
const currentVersion = require('../package.json').version
const versionIncrements = ['patch', 'minor', 'major']
const inc = i => semver.inc(currentVersion, i)
const bin = name => path.resolve(__dirname, `../node_modules/.bin/${name}`)
function run(bin, args, opts = {}) {
return execa(bin, args, { stdio: 'inherit', ...opts })
}
const step = msg => console.log(chalk.cyan(msg))
async function main() {
let targetVersion
const { release } = await prompt({
type: 'select',
name: 'release',
message: 'Select release type',
choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom']),
})
if (release === 'custom') {
targetVersion = (
await prompt({
type: 'input',
name: 'version',
message: 'Input custom version',
initial: currentVersion,
})
).version
} else {
targetVersion = release.match(/\((.*)\)/)[1]
}
if (!semver.valid(targetVersion))
throw new Error(`Invalid target version: ${targetVersion}`)
const { yes: tagOk } = await prompt({
type: 'confirm',
name: 'yes',
message: `Releasing v${targetVersion}. Confirm?`,
})
if (!tagOk)
return
// Update the package version.
step('\nUpdating the package version...')
updatePackage(targetVersion)
// Build the package.
step('\nBuilding the package...')
await run('yarn', ['build'])
// Generate the changelog.
step('\nGenerating the changelog...')
await run('yarn', ['changelog'])
await run('yarn', ['prettier', '--write', 'CHANGELOG.md'])
const { yes: changelogOk } = await prompt({
type: 'confirm',
name: 'yes',
message: `Changelog generated. Does it look good?`,
})
if (!changelogOk)
return
// Commit changes to the Git and create a tag.
step('\nCommitting changes...')
await run('git', ['add', 'CHANGELOG.md', 'package.json'])
await run('git', ['commit', '-m', `release: v${targetVersion}`])
await run('git', ['tag', `v${targetVersion}`])
// Publish the package.
step('\nPublishing the package...')
await run('yarn', [
'publish',
'--new-version',
targetVersion,
'--no-commit-hooks',
'--no-git-tag-version',
])
await run('npm', [
'publish',
'--registry',
'https://registry.npmjs.org',
'--access',
'public',
])
// Push to GitHub.
step('\nPushing to GitHub...')
await run('git', ['push', 'origin', `refs/tags/v${targetVersion}`])
await run('git', ['push'])
}
function updatePackage(version) {
const pkgPath = path.resolve(path.resolve(__dirname, '..'), 'package.json')
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
pkg.version = version
fs.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`)
}
main().catch(err => console.error(err))
Semver:
- Patch release: bugfix and other minor changes.
- Minor release: new features not breaking API (backward compatible).
- Major release: new features breaking API (not backward compatible).
- Alpha (α): 预览版 (内部测试版), 会有很多 Bug, 一般只有测试人员使用.
- Beta (β): 测试版 (或者叫公开测试版), 会一直加入新的功能.
- RC (Release Candidate): 最终测试版本, 可能成为最终产品的候选版本.
- 多数开源软件会推出两个 RC 版本, 最后的 RC2 则成为正式版本.
npm version patch
npm publish
npm version minor
npm publish
npm version major
npm publish
Link
cd path/to/my-project
npm link path/to/my-utils
# in local B package, build local B binary (npm install -g B)
npm link
# in local A package, set `B` link in package.json to local B binary
npm link B
Security
npm audit fix
npm audit fix --force
NPX
Run local node_modules:
npm install eslint -D
npx eslint .
Run global package (not installed):
npx create-react-app react-app
Run specific version:
npx -p package1@next -p package2@next -c "command"
Run scripts with different node version:
npx -p node@version -- node index.js
Run remote repo/gist code:
npx user/repo#branch
npx gistUrl
NPX cache packages in ~/.npm/_npx.
To get latest version package:
# https://github.com/return-0x0/node-clear-npx-cache.
# https://github.com/npm/cli/issues/2329.
# https://github.com/npm/cli/issues/2395.
# https://github.com/npm/cli/pull/2592.
# https://github.com/facebook/create-react-app/issues/10601.
# https://github.com/facebook/create-react-app/issues/12022.
npx clear-npx-cache
npx create-react-app app
Dependencies
- Dependency Nesting/Hell (NPM v1).
- Dependency Flatten/Hoist (NPM v3).
- Dependency Consistent Lockfile (NPM v5 and Yarn).
- Dependency Hard/Symbol Links (PNPM):
- Hard links for global
.pnpmstore to save disk storage. - Symbol links for local require short path with non-flat
node_modulesto rectify doppelgangers and ghost/phantom dependencies problem.
- Hard links for global
peerDependencies: 提示宿主环境去安装满足插件peerDependencies所指定依赖的包, 然后在插件import或者require所依赖的包的时候, 永远都是引用宿主环境统一安装的 NPM 包, 最终解决插件与所依赖包不一致的问题.- 构建依赖树的过程中, 版本确认需要结合
package.json和package-lock.json:- 先确认
package-lock.json安装版本, 符合规则就以此为准, 否则由package.json声明的版本范围重新确认. - 若是在开发中手动更改包信息,
会导致 lockfile 版本信息异常,
也由
package.json重新确认. - 确认好的依赖树会存到
package-lock.json文件中.
- 先确认
- 同一个依赖, 更高版本的包会安装到顶层
node_modules目录, 低版本的包会分散在某些依赖的node_modules目录. - Lockfile 保证项目依赖结构的确定性, 保障项目在多环境运行的稳定性.
Doppelgangers
- Singleton conflict: multiple version of same package in
node_modules. - Types conflict: global
typesnaming conflict.
Ghost
NPM ghost (phantom) dependency:
- Imported packages from
dependencies of dependencies: When updatedependenciesto minor version,dependencies of dependenciesmay get major BREAKING version (It's legal forsemver, whendependenciesAPI don't change). - Imported packages from
devDependencies: When others install your library, such imported packages will missing, cause they aren't located in librarypackage.json. - Imported packages from root
node_modulesin monorepo. When others install your library, such imported packages will missing, cause they aren't located in librarypackage.json.
Invalid
$ npm ls
package@version invalid
Modify package-lock.json
to remove locked invalid package version.
package.json
Bin
当设置了 bin 字段后,
在 package.json script 字段中,
可以使用简写编写命令
(但是局部安装无法在 shell 下使用, 需 npx <bin-name>).
Version
npm version major
npm version minor
npm version patch
Workspaces
In root package.json:
{
"workspaces": [
"./packages/*",
"./css/*",
"./angular/*",
"./react/*",
"./vue/*"
]
}
In root cwd:
npm i
npm run lint -ws
npm run test -w package-a
npm i lodash -w package-b
npm i -D eslint -w package-c
Exports
exports can define public API:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"module": "./dist/index.mjs",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.mjs"
},
"./package.json": "./package.json"
},
"types": "./dist/index.d.ts",
"browser": "./dist/index.mjs",
"module": "./dist/index.mjs",
"main": "./dist/index.cjs"
}
exports configures JavaScript level,
file packages/rest/build/gen/util/regexp-tools.js
can be imported via @github/rest/gen/util/regexp-tools:
- Don't need to mention directory
build/distin module specifiers. - Don't need to mention
.js/.tsin module specifiers.
{
"name": "@github/rest",
"private": true,
"exports": {
"./": "./dist/index.js",
"./gen/*": "./dist/gen/*.js",
"./client/*": "./dist/client/*.js",
"./contract": "./dist/contract.js",
"./state": "./dist/state.js",
"./package.json": "./package.json"
}
}
import octokit from '@github/rest'
import { Contract } from '@github/rest/contract'
import utils from '@github/rest/gen/utils'
import state from '@github/rest/state'
Resolutions
Besides git bisect for debugging broken version,
revert to last working version with resolutions field
will help to fix broken version too:
{
"resolutions": {
"rc-field-form": "1.44.0"
}
}
Lockfile
Dependency pinning:
When kept in sync with its associated package.json,
a lockfile will further lock down the exact dependencies and sub-dependencies,
so that everyone running npm i or yarn will install the exact same dependencies.
If the package.json contains a range,
and a new in-range version is released that would break the build,
then essentially package.json is in a state of broken,
even if the lockfile is still holding things together.
- Apps (web or Node.js) that aren't
require()by other packages should pin all types of dependencies for greatest reliability/predictability. - Libraries that are
consumed/required()by others should keep using SemVer ranges for dependencies (purge multiple versionnode_modules) but can use pinned devDependencies.
Environment
配置文件以 .env/JS(Object)/JSON/JSONP/XML/YML 格式单独存放,
方便读取.
# .env file (added to .gitignore)
NODE_ENV=development
PORT=8626
# Set your database/API connection information here
API_KEY=**************************
API_URL=**************************
// config.js
const dotenv = require('dotenv')
dotenv.config()
module.exports = {
endpoint: process.env.API_URL,
masterKey: process.env.API_KEY,
port: process.env.PORT,
}
// server.js
const { port } = require('./config')
console.log(`Your port is ${port}`) // 8626