npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@guoyanhong723/antd

v1.0.0

Published

React组件库

Downloads

4

Readme

AntDesign

1. 技术栈

  • 框架 react
  • 测试 jest+enzyme
  • 检查 eslint
  • 打包 webpack + gulp
  • 文档 bisheng
  • 钩子 Husky

2. 源码目录

  • .husky git 钩子
  • _site 网站
  • components 组件
  • docs 文档
  • dist 打包生成的文件
  • es es6
  • lib es5
  • scripts 脚本
  • site 组件预览项目
  • tests 测试
  • typing 类型定义

3. 内容

  • webpack 配置
  • storybook 文档和组件编写
  • 单元测试+E2E 快照测试+ 代码覆盖率
  • eslint + prettier + editorconfig
  • git hook
  • 编译发布
  • 持续集成

一、创建项目

1. 创建文件夹

mkdir antd && cd antd
npm init -y

2. package.json

{
  "name": "@gyh/antd",
  "version": "1.1.2",
  "description": "React组件库",
  "main": "lib/index.js",
  "module": "es/index.js",
  "unpkg": "dist/antd.js",
  "typings": "lib/index.d.ts",
  "files": ["dist", "es", "lib"],
  "scripts": {
    "build": "webpack",
    "storybook": "start-storybook -p 7006",
    "build-storybook": "build-storybook",
    "test:unit": "jest --config unit.jest.js",
    "test:e2e": "jest --config e2e.jest.js",
    "test": "npm run test:unit && npm run test:e2e",
    "compile": "gulp compile",
    "patch": "npm version patch"
  },
  "publishConfig": {
    "access": "public",
    "registry": "http://registry.npmjs.org"
  },
  "keywords": ["react", "Component"],
  "author": "guoyanhong",
  "license": "MIT",
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "peerDependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "devDependencies": {
    "@typescript-eslint/parser": "^4.31.1",
    "and": "^0.0.3",
    "eslint": "^7.32.0",
    "eslint-config-airbnb": "^18.2.1",
    "eslint-plugin-import": "^2.24.2",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-react": "^7.25.2",
    "eslint-plugin-react-hooks": "^4.2.0",
    "husky": "^7.0.2",
    "@babel/core": "^7.15.5",
    "@babel/plugin-transform-runtime": "^7.15.0",
    "@babel/plugin-transform-typescript": "^7.15.4",
    "@babel/preset-env": "^7.15.6",
    "@babel/preset-react": "^7.14.5",
    "@commitlint/cli": "^13.1.0",
    "@commitlint/config-conventional": "^13.1.0",
    "@storybook/addon-essentials": "^6.3.8",
    "@storybook/react": "^6.3.8",
    "@types/enzyme": "^3.10.9",
    "@types/jest": "^27.0.1",
    "@types/jest-environment-puppeteer": "^4.4.1",
    "@types/jest-image-snapshot": "^4.3.1",
    "@types/node": "^16.9.2",
    "@types/puppeteer": "^5.4.4",
    "@types/react": "^17.0.21",
    "@types/react-dom": "^17.0.9",
    "@wojtekmaj/enzyme-adapter-react-17": "^0.6.3",
    "autoprefixer": "^10.3.4",
    "babel-loader": "^8.2.2",
    "commitizen": "^4.2.4",
    "css-loader": "^6.2.0",
    "cz-customizable": "^6.3.0",
    "enzyme": "^3.11.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-prettier": "^4.0.0",
    "gulp": "^4.0.2",
    "gulp-babel": "^8.0.0",
    "gulp-typescript": "^6.0.0-alpha.1",
    "jest": "^27.2.0",
    "jest-environment-puppeteer": "^5.0.4",
    "jest-image-snapshot": "^4.5.1",
    "jest-puppeteer": "^5.0.4",
    "less": "^4.1.1",
    "less-loader": "^10.0.1",
    "merge2": "^1.4.1",
    "mini-css-extract-plugin": "^2.3.0",
    "postcss-loader": "^6.1.1",
    "prettier": "^2.4.1",
    "puppeteer": "^10.2.0",
    "rimraf": "^3.0.2",
    "typescript": "^4.4.3",
    "webpack": "^5.53.0",
    "webpack-cli": "^4.8.0"
  }
}

二、配置 webpack

2.1 安装依赖

yarn

2.2 webpack.config.js

const path = require('path')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const cwd = process.cwd()

module.exports = {
  mode: 'development', //开发模式
  devtool: false, //关闭生成sourcemap
  entry: {
    antd: './index.js'
  },
  output: {
    path: path.resolve('dist'), //输出到dist目录
    filename: '[name].js', //打包后的文件 antd.css
    library: 'antd', //打包后库的名字
    libraryTarget: 'umd' //打包后模块的格式 umd amd cmd commonjs commonjs window
  },
  externals: {
    //组件库代码其实是不需要打包react 和react-dom进去的
    react: {
      //外部依赖
      root: 'React',
      commonjs2: 'react',
      commonjs: 'react',
      amd: 'react'
    },
    'react-dom': {
      root: 'ReactDOM',
      commonjs2: 'react-dom',
      commonjs: 'react-dom',
      amd: 'react-dom'
    }
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'] //指定扩展名
    // alias: {
    //     antdesign: cwd
    // }
  },
  module: {
    rules: [
      {
        test: /\.(j|t)sx?$/, //配置如何加载js ts jsx tsx
        exclude: /node_modules/,
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader, //把这些CSS收集起来后面通过插件写入单独的antd.css
          {
            loader: 'css-loader', //处理@import和url
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader', //加厂商前缀
            options: {
              postcssOptions: {
                plugins: ['autoprefixer']
              },
              sourceMap: true
            }
          }
        ]
      },
      {
        test: /\.less$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: 'css-loader',
            options: {
              sourceMap: true
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: ['autoprefixer']
              },
              sourceMap: true
            }
          },
          {
            loader: 'less-loader', //把less编译 成css
            options: {
              lessOptions: {
                javascriptEnabled: true
              },
              sourceMap: true
            }
          }
        ]
      },
      {
        //webpack5里file-loaer url-loader已经废弃 了
        test: /\.(png|jpg|jpeg|gif|svg)(\?v=\d+\.\d+\.\d+)?$/i,
        type: 'asset' //静态文件不再需要配置loader
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css'
    })
  ]
}

2.3 babel.config.js

module.exports = {
  presets: [
    '@babel/preset-react',
    [
      '@babel/preset-env',
      {
        modules: 'auto',
        targets: {
          browsers: ['last 2 versions', 'Firefox ESR', '> 1%', 'ie >= 11']
        }
      }
    ]
  ],
  plugins: [
    [
      '@babel/plugin-transform-typescript',
      {
        isTSX: true
      }
    ],
    ['@babel/plugin-transform-runtime']
  ]
}

2.4 .gitignore

*.iml
.idea/
.ipr
.iws
*~
~*
*.diff
*.patch
*.bak
.DS_Store
Thumbs.db
.project
.*proj
.svn/
*.swp
*.swo
*.log
*.log.*
*.json.gzip
node_modules/
.buildpath
.settings
npm-debug.log
nohup.out
_site
_data
dist
report.html
/lib
/es
elasticsearch-*
config/base.yaml
/.vscode/
/coverage
yarn.lock
package-lock.json
components/**/*.js
components/**/*.jsx
!components/**/__tests__/**/*.js
!components/**/__tests__/**/*.js.snap
/.history
*.tmp

# Docs templates
site/theme/template/Color/ColorPicker.jsx
site/theme/template/IconDisplay/*.js
site/theme/template/IconDisplay/*.jsx
site/theme/template/IconDisplay/fields.js
site/theme/template/Home/**/*.jsx
site/theme/template/utils.jsx
site/theme/template/Layout/Footer.jsx
site/theme/template/Layout/Header/**/*.jsx
site/theme/template/Layout/SiteContext.jsx
site/theme/template/Content/Article.jsx
site/theme/template/Content/EditButton.jsx
site/theme/template/Resources/*.jsx
site/theme/template/Resources/**/*.jsx
site/theme/template/NotFound.jsx
scripts/previewEditor/index.html
components/version/version.tsx

# Image snapshot diff
__diff_output__/
__image_snapshots__/
/jest-stare
/imageSnapshots
/imageDiffSnapshots
storybook-static

sh.exe.stackdump
/snapshots
/diffSnapshots

2.5tsconfig.json

{
  "compilerOptions": {
    "strictNullChecks": true,
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "jsx": "react",
    "noUnusedParameters": true,
    "noUnusedLocals": true,
    "noImplicitAny": true,
    "target": "es6",
    "lib": ["dom", "es2017"],
    "skipLibCheck": true,
    "types": ["node"]
  },
  "exclude": ["node_modules", "lib", "es"]
}

2.6 index.js

module.exports = require('./components')

2.7 components\index.tsx

import Button from './button'
export { ButtonProps } from './button'
export { Button }

2.8 button\index.tsx

import Button from './button'
export default Button
export { ButtonProps } from './button'

2.9 button.tsx

import React, { ButtonHTMLAttributes } from 'react'
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {}

const Button: React.FC<ButtonProps> = props => {
  const { children } = props
  return <button type='button'>{children}</button>
}

export default Button

// 如果你导出的是type,会保证在编译去掉
export { ButtonProps }

三、storybook

3.1 安装

yarn add @storybook/addon-essentials @storybook/react -D

3.2 storybook\main.js

module.exports = {
  stories: [
    '../components/Introduction.stories.mdx',
    '../components/Install.stories.mdx',
    '../components/Components.stories.mdx',
    '../components/**/*.stories.mdx',
    '../components/**/*.stories.@(js|jsx|ts|tsx)'
  ],
  addons: ['@storybook/addon-essentials']
}

3.3 Introduction.stories.mdx

<Meta title='开始/介绍' />

## Ant Design of React

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

3.4 Install.stories.mdx

<Meta title='开始/安装使用' />

## 安装

使用 npm 或 yarn 安装

## 浏览器引入

在浏览器中使用 script 和 link 标签直接引入文件,并使用全局变量 ant
我们在 npm 发布包内的 antd/dist 目录下提供了 antd.js

## 示例

3.5 Components.stories.mdx

<Meta title='开始/组件总览' />

## 组件总览

antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。

## 通用

- Button 按钮
- Icon 图标
- Typography 排版

## 布局

- Divider 分割线
- Grid 栅格
- Layout 布局
- Space 间距

## 导航

- Affix 固钉
- Breadcrumb 面包屑
- Dropdown 下拉菜单
- Menu 导航菜单
- Pagination 分页
- PageHeader 页头
- Steps 步骤条

## 数据录入

- AutoComplete 自动完成
- Checkbox 多选框
- Cascader 级联选择
- DatePicker 日期选择框
- Form 表单
- InputNumber 数字输入框
- Input 输入框
- Mentions 提及
- Rate 评分
- Radio 单选框
- Switch 开关
- Slider 滑动输入条
- Select 选择器
- TreeSelect 树选择
- Transfer 穿梭框
- TimePicker 时间选择框
- Upload 上传

## 数据展示

- Avatar 头像
- Badge 徽标数
- Comment 评论
- Collapse 折叠面板
- Carousel 走马灯
- Card 卡片
- Calendar 日历
- Descriptions 描述列表
- Empty 空状态
- Image 图片
- List 列表
- Popover 气泡卡片
- Statistic 统计数值
- Tree 树形控件
- Tooltip 文字提示
- Timeline 时间轴
- Tag 标签
- Tabs 标签页
- Table 表格

## 反馈

- Alert 警告提示
- Drawer 抽屉
- Modal 对话框
- Message 全局提示
- Notification 通知提醒框
- Progress 进度条
- Popconfirm 气泡确认框
- Result 结果
- Spin 加载中
- Skeleton 骨架屏

## 其他

- Anchor 锚点
- BackTop 回到顶部
- ConfigProvider 全局化配置

3.6 button.stories.tsx

import { ComponentMeta, ComponentStory } from '@storybook/react'

import Button from '.'
import React from 'react'

// 元数据,button描述信息
export default {
  title: '通用/Button',
  component: Button
} as ComponentMeta<typeof Button>

const Template: ComponentStory<typeof Button> = args => <Button {...args} />

export const Basic = Template.bind({})
Basic.args = {
  children: '按钮'
}

3.7 package.json

    "storybook": "start-storybook -p 7006",
    "build-storybook": "build-storybook",

四、测试

4.1 安装

  • jest 是一个令人愉快的 JavaScript 测试框架
  • Enzyme 用于 React 的 JS 测试工具
  • puppeteer 是一个控制 headless Chrome 的 Node.js API
  • jest-image- snapshot 执行图像比较的 Jest 匹配器,对于视觉回归测试非常有用
yarn add jest @types/jest @wojtekmaj/enzyme-adapter-react-17 puppetter @types/puppetter jest-environment-puppetter @types/jest-environment-puppetter jest-puppetter jest-image-snapshot @types/jest-image-snapshot -D

yarn add enzyme @types/enzyme -D

4.2 tests\setup.js

tests\setup.js

const React = require('react')
const Enzyme = require('enzyme')

const Adapter = require('@wojtekmaj/enzyme-adapter-react-17')
Enzyme.configure({ adapter: new Adapter() })

4.3 tests\index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Amazing Antd</title>
    <style>
      body {
        border: 5px solid #1890ff;
      }
    </style>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

4.4 unit.jest.js

module.exports = {
  verbose: true,
  testEnvironment: 'jsdom', //运行测试的环境
  setupFiles: ['./tests/setup.js'],
  testMatch: ['**/unit/**/*.(spec|test).(js|ts|jsx|tsx)'],
  collectCoverage: true,
  collectCoverageFrom: [
    'components/**/*.(js|ts|jsx|tsx)',
    '!components/**/*.stories.(js|ts|jsx|tsx)',
    '!components/**/*.(spec|test).(js|ts|jsx|tsx)'
  ]
}

4.5 e2e.jest.js

module.exports = {
  verbose: true,
  testEnvironment: 'jest-environment-puppeteer',
  setupFiles: ['./tests/setup.js'],
  preset: 'jest-puppeteer',
  testMatch: ['**/e2e/**/*.(spec|test).(j|t)sx']
}

4.6 unit\index.test.tsx

import Button from '..'
import React from 'react'
import { mount } from 'enzyme'
describe('测试Button', () => {
  it('测试是Button是否能够正确挂载', () => {
    //挂载组件的过程不可抛错误
    expect(() => mount(<Button>Follow</Button>)).not.toThrow()
  })
})

4.7 snapshot.spec.tsx

import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { configureToMatchImageSnapshot } from 'jest-image-snapshot'
import Button from '..'
const toMatchSnapshot = configureToMatchImageSnapshot({
  customSnapshotsDir: `${process.cwd()}/snapshots`,
  customDiffDir: `${process.cwd()}/diffSnapshots`
})
expect.extend({ toMatchSnapshot })
describe('测试Buttoon快照', () => {
  it('测试快照是否正确', async () => {
    await jestPuppeteer.resetPage()
    await page.goto(`file://${process.cwd()}/tests/index.html`)
    const html = ReactDOMServer.renderToString(<Button>按钮222</Button>)
    await page.evaluate(innerHTML => {
      document.querySelector('#root').innerHTML = innerHTML
    }, html)
    const screenshot = await page.screenshot() //生成一张新的快照
    expect(screenshot).toMatchSnapshot() //比较 新的快照和老的快照是否相相同
  })
})
//reacdt单元测试 testLibrary  antd

4.8 jest-puppetter.config.js

module.exports = {
  launch: {
    headless: true
  },
  browserContext: 'default'
}

4.9 package.json

    "test:unit": "jest --config unit.jest.js",
    "test:e2e": "jest --config e2e.jest.js",
    "test": "npm run test:unit && npm run test:e2e",

五、eslint

  • elsint 是一个插件话并且可配置的 JavaScript 预发规则和代码风格的检查工具
  • eslint-config-airbnb Airbnb 提供的 eslint 配置

5.1 安装

 yarn add @typescript-eslint/parser elsint eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx eslint-config-airbnb -D

5.2 .eslintrc

.eslintrc.js

module.exports = {}

5.3 .eslintignore

5.4 packag.json

六、prettier

6.1 安装依赖

6.2 .eslintrc.js

6.3 prettierrc

6.4 button\index.tsx

6.5 settings.json

七、editorconfig

7.1 .editorconfig

八、git hook

  • Git 钩子能在特定的重要动作发生时触发自定义的脚本
  • husky 可以让我们向项目中方便添加 git hooks
  • lint-staged 用于实现每次提交只检查本次提交所修改的文件

8.1 安装

yarn add husky -D
npm set-script prepare "husky install"
npm run prepare

8.2 pre-commit

  • pre-commit 在 git add 提交之后,然后执行 git commit 时执行,脚本执行没报错就继续提交,反之就驳回提交的操作
  • 可以在 git commit 之前检查代码,保证所有提交到版本库中的代码都是符合规定的

8.2.1 安装脚本

npx husky add .husky/pre-commit "npx lint-staged"

8.2.2 .lintstagedrc

{
  "*.{js,ts,jsx,tsx}": "eslint"
}

8.3 commit-msg

8.3.1 安装依赖

8.3.2 安装脚本

8.3.3 cz-config.js

8.4 pre-push

九、编译发布

  • rimraf 是 node 版本的 rm -rf
  • gulp 将开发流程中让人痛苦或耗时的任务自动化,从而减少你所浪费的时间、创造更大的价值
  • merge2 合并多个流为同一个

9.1 安装依赖

yarn add rimraf gulp gulp-typescript gulp-babel merge2 -D

npm verson patch
npm publish
cat ~/.npmrc

9.2 gulpfile.js

const gulp = require('gulp') //定义执行任务
const path = require('path') //处理路径
const rimraf = require('rimraf') //删除跑路的 rm -rf
const ts = require('gulp-typescript')
const babel = require('gulp-babel')
const merge2 = require('merge2') //Promise.all
const { compilerOptions } = require('./tsconfig.json')

const tsConfig = {
  noUnusedParameters: true, //不能有未使用的参数
  noUnusedLocals: true, //不能有未使用的本地变量
  strictNullChecks: true, //严格的Null检查
  target: 'es6', //编译 的目标
  jsx: 'react', //jsx如何处理preserve 保留不处理  react变成React.createElement()
  moduleResolution: 'node', //模块的查找规则 node
  declaration: true, //生成声明文件 d.ts
  allowSyntheticDefaultImports: true, //允许 默认导入
  ...compilerOptions
}
const babelConfig = require('./babel.config')
//准备好要编译 的文件
//glob 文件匹配模板,类似于正则
const source = [
  'components/**/*.{js,ts,jsx,tsx}',
  '!components/**/*.stories.{js,ts,jsx,tsx}',
  '!components/**/e2e/*',
  '!components/**/unit/*'
]
//antd\components
const base = path.join(process.cwd(), 'components')
function getProjectPath (filePath) {
  return path.join(process.cwd(), filePath)
}
// antd\lib
const libDir = getProjectPath('lib')
// antd\es
const esDir = getProjectPath('es')
/**
 * 执行编译
 * @param {*} modules 是否要转换模块
 */
function compile (modules) {
  const targeDir = modules === false ? esDir : libDir
  rimraf.sync(targeDir) //删除老的内容 rm -rf
  //把文件匹配模式传给gulp,gulp会按这个模式把文件匹配了出来
  //ts转译后会生成二个流,一个流是JS一个流是类型声明d.ts
  const { js, dts } = gulp.src(source, { base }).pipe(ts(tsConfig))
  const dtsStream = dts.pipe(gulp.dest(targeDir))
  let jsStream = js
  if (modules) {
    //如果要转成ES5,就用babel进行转义
    jsStream = js.pipe(babel(babelConfig))
  }
  jsStream = jsStream.pipe(gulp.dest(targeDir))
  return merge2([jsStream, dtsStream])
}
gulp.task('compile-with-es', done => {
  console.log('compile to es')
  compile(false).on('finish', done)
})
gulp.task('compile-with-lib', done => {
  console.log('compile to js')
  compile().on('finish', done)
})
gulp.task('compile', gulp.parallel('compile-with-es', 'compile-with-lib'))

9.3 package.json


十、持续集成

10.1 .travis.yml