Appearance
npm 实战案例
本章将通过实际项目案例展示如何在真实场景中应用npm,包括完整的项目搭建、依赖管理、工作流配置等。
案例一:全栈Web应用项目
项目结构设计
fullstack-app/
├── package.json
├── .npmrc
├── .nvmrc
├── .gitignore
├── README.md
├── frontend/
│ ├── package.json
│ ├── src/
│ ├── public/
│ └── vite.config.js
├── backend/
│ ├── package.json
│ ├── src/
│ ├── routes/
│ └── middleware/
├── shared/
│ ├── package.json
│ └── types/
├── scripts/
│ ├── setup.js
│ └── health-check.js
└── docker/
├── Dockerfile.frontend
├── Dockerfile.backend
└── docker-compose.yml
根目录package.json配置
json
{
"name": "fullstack-app",
"private": true,
"version": "1.0.0",
"description": "Full-stack application with npm workspaces",
"workspaces": [
"frontend",
"backend",
"shared"
],
"scripts": {
"dev": "concurrently \"npm run dev --workspace=frontend\" \"npm run dev --workspace=backend\"",
"dev:frontend": "npm run dev --workspace=frontend",
"dev:backend": "npm run dev --workspace=backend",
"build": "npm run build --workspaces",
"build:frontend": "npm run build --workspace=frontend",
"build:backend": "npm run build --workspace=backend",
"test": "npm run test --workspaces",
"test:frontend": "npm run test --workspace=frontend",
"test:backend": "npm run test --workspace=backend",
"lint": "npm run lint --workspaces",
"format": "prettier --write . && npm run format --workspaces",
"setup": "node scripts/setup.js",
"health": "node scripts/health-check.js",
"docker:build": "docker-compose -f docker/docker-compose.yml build",
"docker:up": "docker-compose -f docker/docker-compose.yml up -d",
"docker:down": "docker-compose -f docker/docker-compose.yml down"
},
"devDependencies": {
"concurrently": "^7.6.0",
"prettier": "^2.8.0"
},
"engines": {
"node": ">=16.14.0",
"npm": ">=8.0.0"
},
"volta": {
"node": "18.12.1"
},
"keywords": [
"fullstack",
"javascript",
"react",
"express",
"monorepo"
],
"author": "Development Team",
"license": "MIT"
}
共享包配置 (shared/package.json)
json
{
"name": "@fullstack-app/shared",
"version": "1.0.0",
"description": "Shared types and utilities for fullstack app",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./types": {
"import": "./dist/types.mjs",
"require": "./dist/types.js",
"types": "./dist/types.d.ts"
}
},
"files": [
"dist/",
"README.md"
],
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
"lint": "eslint src/ --ext .ts",
"test": "vitest run",
"test:watch": "vitest"
},
"devDependencies": {
"typescript": "^4.9.0",
"tsup": "^6.5.0",
"eslint": "^8.30.0",
"vitest": "^0.26.0",
"@types/node": "^18.11.18"
}
}
前端配置 (frontend/package.json)
json
{
"name": "@fullstack-app/frontend",
"version": "1.0.0",
"description": "Frontend application",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"lint": "eslint src/ --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint src/ --ext .ts,.tsx,.js,.jsx --fix",
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
"test": "vitest",
"test:ui": "vitest --ui",
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"type-check": "tsc --noEmit"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"axios": "^1.2.0",
"@fullstack-app/shared": "workspace:*"
},
"devDependencies": {
"@vitejs/plugin-react": "^3.0.0",
"vite": "^4.0.0",
"typescript": "^4.9.0",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.10",
"eslint": "^8.30.0",
"prettier": "^2.8.0",
"vitest": "^0.26.0",
"@vitest/ui": "^0.26.0",
"@testing-library/react": "^13.4.0",
"@testing-library/jest-dom": "^5.16.5"
},
"volta": {
"node": "18.12.1"
}
}
后端配置 (backend/package.json)
json
{
"name": "@fullstack-app/backend",
"version": "1.0.0",
"description": "Backend API server",
"main": "dist/index.js",
"scripts": {
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"start:prod": "NODE_ENV=production node dist/index.js",
"lint": "eslint src/ --ext .ts",
"lint:fix": "eslint src/ --ext .ts --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"migrate": "ts-node scripts/migrate.ts",
"seed": "ts-node scripts/seed.ts",
"type-check": "tsc --noEmit",
"security": "npm audit --audit-level moderate"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"helmet": "^6.0.1",
"bcryptjs": "^2.4.3",
"jsonwebtoken": "^9.0.0",
"mongoose": "^6.8.1",
"@fullstack-app/shared": "workspace:*"
},
"devDependencies": {
"@types/express": "^4.17.15",
"@types/node": "^18.11.18",
"@types/bcryptjs": "^2.4.2",
"@types/jsonwebtoken": "^9.0.1",
"typescript": "^4.9.4",
"nodemon": "^2.0.20",
"ts-node": "^10.9.1",
"eslint": "^8.30.0",
"jest": "^29.3.1",
"@types/jest": "^29.2.4",
"supertest": "^6.3.3",
"@types/supertest": "^2.0.12"
},
"volta": {
"node": "18.12.1"
}
}
案例二:CLI工具开发
CLI项目结构
my-cli-tool/
├── package.json
├── bin/
│ └── my-cli
├── src/
│ ├── cli.js
│ ├── commands/
│ │ ├── init.js
│ │ ├── build.js
│ │ └── deploy.js
│ └── utils/
│ ├── logger.js
│ └── config.js
├── templates/
│ ├── react/
│ └── vue/
├── docs/
└── tests/
CLI工具package.json
json
{
"name": "my-cli-tool",
"version": "1.0.0",
"description": "A custom CLI tool for project scaffolding",
"bin": {
"my-cli": "./bin/my-cli"
},
"scripts": {
"dev": "node ./bin/my-cli",
"build": "echo 'No build needed for CLI tool'",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/ --ext .js",
"lint:fix": "eslint src/ --ext .js --fix",
"prepublishOnly": "npm test && npm run lint",
"release:patch": "npm version patch && npm publish",
"release:minor": "npm version minor && npm publish",
"release:major": "npm version major && npm publish"
},
"dependencies": {
"commander": "^9.4.1",
"inquirer": "^8.2.5",
"chalk": "^4.1.2",
"fs-extra": "^11.1.0",
"handlebars": "^4.7.7",
"minimist": "^1.2.7"
},
"devDependencies": {
"jest": "^29.3.1",
"eslint": "^8.30.0",
"prettier": "^2.8.1"
},
"keywords": [
"cli",
"scaffolding",
"generator",
"tool"
],
"author": "Developer",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
},
"files": [
"bin/",
"src/",
"templates/",
"README.md",
"LICENSE"
],
"publishConfig": {
"access": "public"
}
}
CLI入口文件 (bin/my-cli)
bash
#!/usr/bin/env node
// 检查Node.js版本
const semver = require('semver');
const packageJson = require('../package.json');
if (!semver.satisfies(process.version, packageJson.engines.node)) {
console.error(`Node.js ${packageJson.engines.node} is required. Current version: ${process.version}`);
process.exit(1);
}
// 执行CLI
require('../src/cli.js');
CLI核心实现 (src/cli.js)
javascript
#!/usr/bin/env node
const { Command } = require('commander');
const chalk = require('chalk');
const { initCommand } = require('./commands/init');
const { buildCommand } = require('./commands/build');
const { deployCommand } = require('./commands/deploy');
const program = new Command();
program
.name('my-cli')
.description(chalk.blue('CLI tool for project scaffolding and management'))
.version(require('../package.json').version);
program.addCommand(initCommand);
program.addCommand(buildCommand);
program.addCommand(deployCommand);
// 处理未知命令
program.on('command:*', () => {
console.error(chalk.red(`\nError: unknown command: ${program.args.join(' ')}\n`));
program.outputHelp();
process.exit(1);
});
// 全局错误处理
process.on('unhandledRejection', (reason, promise) => {
console.error(chalk.red('Unhandled Rejection at:'), promise, 'reason:', reason);
process.exit(1);
});
program.parse(process.argv);
案例三:库/包开发
库项目结构
my-awesome-lib/
├── package.json
├── tsconfig.json
├── rollup.config.js
├── src/
│ ├── index.ts
│ ├── utils/
│ │ ├── helper.ts
│ │ └── validator.ts
│ └── types/
│ └── index.ts
├── dist/
├── docs/
├── benchmarks/
└── tests/
├── unit/
├── integration/
└── fixtures/
库package.json
json
{
"name": "my-awesome-lib",
"version": "1.0.0",
"description": "An awesome utility library",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.mjs",
"browser": "dist/umd/my-awesome-lib.min.js",
"types": "dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/esm/index.mjs",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
},
"./utils": {
"import": "./dist/esm/utils/index.mjs",
"require": "./dist/cjs/utils/index.js"
}
},
"files": [
"dist/",
"README.md",
"LICENSE",
"package.json"
],
"scripts": {
"prebuild": "npm run clean",
"build": "rollup -c",
"build:dev": "rollup -c -w",
"build:umd": "rollup -c --environment FORMAT:umd",
"build:min": "rollup -c --environment MINIFY:true",
"clean": "rimraf dist",
"test": "jest",
"test:unit": "jest tests/unit/",
"test:integration": "jest tests/integration/",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src/ --ext .ts",
"lint:fix": "eslint src/ --ext .ts --fix",
"format": "prettier --write \"src/**/*.{ts,js,json,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,js,json,md}\"",
"type-check": "tsc --noEmit",
"bench": "node benchmarks/index.js",
"docs": "typedoc --out docs/api src/",
"prepublishOnly": "npm run test && npm run build && npm run type-check",
"release": "standard-version",
"release:minor": "standard-version --release-as minor",
"release:patch": "standard-version --release-as patch",
"release:major": "standard-version --release-as major"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.0.0",
"@rollup/plugin-commonjs": "^24.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"rollup": "^3.9.1",
"rollup-plugin-terser": "^7.0.2",
"typescript": "^4.9.4",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"@types/jest": "^29.2.4",
"eslint": "^8.30.0",
"prettier": "^2.8.1",
"rimraf": "^3.0.2",
"typedoc": "^0.23.24",
"standard-version": "^9.5.0",
"@types/node": "^18.11.18"
},
"keywords": [
"utility",
"javascript",
"typescript",
"library"
],
"author": "Library Author",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
},
"sideEffects": false,
"publishConfig": {
"access": "public"
}
}
案例四:微前端架构
微前端项目结构
micro-frontend-platform/
├── package.json
├── nx.json
├── .npmrc
├── host/
│ ├── package.json
│ └── src/
├── micro-app1/
│ ├── package.json
│ └── src/
├── micro-app2/
│ ├── package.json
│ └── src/
├── shared-components/
│ ├── package.json
│ └── src/
├── shared-utils/
│ ├── package.json
│ └── src/
└── gateway/
├── package.json
└── src/
微前端根package.json
json
{
"name": "micro-frontend-platform",
"private": true,
"version": "1.0.0",
"workspaces": [
"host",
"micro-app1",
"micro-app2",
"shared-components",
"shared-utils",
"gateway"
],
"scripts": {
"dev": "concurrently \"npm run dev --workspace=host\" \"npm run dev --workspace=micro-app1\" \"npm run dev --workspace=micro-app2\"",
"dev:host": "npm run dev --workspace=host",
"dev:micro-apps": "concurrently \"npm run dev --workspace=micro-app1\" \"npm run dev --workspace=micro-app2\"",
"build": "npm run build --workspaces",
"build:all": "npm run build:shared && npm run build:micro-apps && npm run build:host",
"build:shared": "npm run build --workspace=shared-components && npm run build --workspace=shared-utils",
"build:micro-apps": "npm run build --workspace=micro-app1 && npm run build --workspace=micro-app2",
"build:host": "npm run build --workspace=host",
"test": "npm run test --workspaces",
"lint": "npm run lint --workspaces",
"format": "prettier --write . && npm run format --workspaces",
"deploy": "npm run deploy --workspaces",
"analyze": "npm run analyze --workspaces"
},
"devDependencies": {
"concurrently": "^7.6.0",
"prettier": "^2.8.0"
},
"engines": {
"node": ">=16.14.0",
"npm": ">=8.0.0"
}
}
微前端共享组件package.json
json
{
"name": "@micro-platform/shared-components",
"version": "1.0.0",
"description": "Shared UI components for micro-frontend platform",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.mjs",
"types": "dist/types/index.d.ts",
"exports": {
".": {
"import": "./dist/esm/index.mjs",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
},
"./button": {
"import": "./dist/esm/components/button.mjs",
"require": "./dist/cjs/components/button.js"
}
},
"files": [
"dist/",
"README.md"
],
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/ --ext .ts,.tsx",
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
},
"devDependencies": {
"@storybook/react-vite": "^7.0.0",
"@storybook/addon-essentials": "^7.0.0",
"vite": "^4.3.0",
"typescript": "^4.9.0",
"jest": "^29.5.0",
"eslint": "^8.30.0"
}
}
案例五:CI/CD自动化流程
GitHub Actions工作流
yaml
# .github/workflows/test.yml
name: Test Suite
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [16.x, 18.x, 20.x]
include:
- node-version: 18.x
os: ubuntu-latest
run-coverage: true
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
env:
CI: true
- name: Run linting
run: npm run lint
env:
CI: true
- name: Run type checking
run: npm run type-check
env:
CI: true
- name: Run tests
run: |
if [ "${{ matrix.run-coverage }}" = "true" ]; then
npm run test:coverage
else
npm run test
fi
env:
CI: true
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
if: matrix.run-coverage
with:
file: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
yaml
# .github/workflows/release.yml
name: Release
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
id-token: write
jobs:
release:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'skip ci')"
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
registry-url: 'https://registry.npmjs.org/'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Run type checking
run: npm run type-check
- name: Run security audit
run: npm audit --audit-level moderate
- name: Build
run: npm run build
- name: Release
run: |
if npx semantic-release; then
echo "Release successful"
else
echo "No release needed"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
continue-on-error: true
这些实战案例展示了如何在不同类型和规模的项目中有效使用npm,包括全栈应用、CLI工具、库开发、微前端架构和CI/CD流程。