Skip to content

Commit d506dd4

Browse files
author
zhaozhiwen
committed
build(release): 标准化发布流程
1 parent 5d03125 commit d506dd4

File tree

14 files changed

+760
-289
lines changed

14 files changed

+760
-289
lines changed

components/alert/index.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@ import { HappyBox } from '../../doc-comps';
88
import BasicDemo from './demo/1-demo-basic';
99
import BasicDemoCode from '!raw-loader!./demo/1-demo-basic.tsx';
1010

11-
# Alert 警告提示
11+
## Alert 警告提示
1212

1313
警告提示,展现需要关注的信息。
1414

15-
## 代码演示
15+
### 代码演示
1616

17-
### 基本用法
17+
#### 基本用法
1818

1919
<HappyBox code={BasicDemoCode} title="基本用法" desc="使用kind控制Alert类型">
2020
<BasicDemo />
2121
</HappyBox>
2222

23-
## API
23+
### API
2424

2525
| 属性 | 说明 | 类型 | 默认值 |
2626
| ---- | -------- | -------------------------------------------- | ------ |

package.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
"typings": "types/index.d.ts",
66
"main": "lib/index.js",
77
"module": "esm/index.js",
8+
"sideEffects": [
9+
"dist/*",
10+
"esm/**/style/*",
11+
"lib/**/style/*",
12+
"*.less"
13+
],
814
"scripts": {
915
"dev": "docz dev",
1016
"start": "npm run dev",
@@ -13,6 +19,7 @@
1319
"clean": "rimraf types lib esm dist",
1420
"build:types": "tsc -p tsconfig.build.json",
1521
"build": "npm run clean && npm run build:types && gulp",
22+
"release": "ts-node ./scripts/release.ts",
1623
"commit": "git-cz",
1724
"test": "jest",
1825
"test:watch": "jest --watch",
@@ -33,15 +40,9 @@
3340
},
3441
"homepage": "https://github.com/worldzhao/react-ui-library-tutorial#readme",
3542
"peerDependencies": {
36-
"react": ">=16.0.0",
37-
"react-dom": ">=16.0.0"
43+
"react": ">=16.8.0",
44+
"react-dom": ">=16.8.0"
3845
},
39-
"sideEffects": [
40-
"dist/*",
41-
"esm/**/style/*",
42-
"lib/**/style/*",
43-
"*.less"
44-
],
4546
"devDependencies": {
4647
"@babel/core": "^7.7.7",
4748
"@babel/plugin-proposal-class-properties": "^7.7.4",
@@ -62,7 +63,7 @@
6263
"chalk": "^3.0.0",
6364
"commitizen": "^4.0.3",
6465
"cz-conventional-changelog": "^3.0.2",
65-
"docz": "^2.2.0",
66+
"docz": "^2.3.2-alpha.0",
6667
"gatsby-plugin-import": "^2.1.5",
6768
"gatsby-plugin-less": "^3.0.17",
6869
"gatsby-plugin-styled-components": "^3.3.0",
@@ -89,10 +90,12 @@
8990
"react-tooltip": "^4.2.5",
9091
"react-use": "^13.12.2",
9192
"rimraf": "^3.0.0",
93+
"semver": "^7.3.2",
9294
"snapshot-diff": "^0.6.1",
9395
"styled-components": "^5.1.0",
9496
"through2": "^3.0.1",
9597
"ts-jest": "^24.2.0",
98+
"ts-node": "^8.10.1",
9699
"typescript": "^3.7.3"
97100
},
98101
"lint-staged": {

scripts/release.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/* eslint-disable import/no-extraneous-dependencies,@typescript-eslint/camelcase, no-console */
2+
import inquirer from 'inquirer';
3+
import fs from 'fs';
4+
import path from 'path';
5+
import child_process from 'child_process';
6+
import util from 'util';
7+
import chalk from 'chalk';
8+
import semverInc from 'semver/functions/inc';
9+
import { ReleaseType } from 'semver';
10+
11+
import pkg from '../package.json';
12+
13+
const exec = util.promisify(child_process.exec);
14+
15+
const run = async (command: string) => {
16+
console.log(chalk.green(command));
17+
await exec(command);
18+
};
19+
20+
const currentVersion = pkg.version;
21+
22+
const getNextVersions = (): { [key in ReleaseType]: string | null } => ({
23+
major: semverInc(currentVersion, 'major'),
24+
minor: semverInc(currentVersion, 'minor'),
25+
patch: semverInc(currentVersion, 'patch'),
26+
premajor: semverInc(currentVersion, 'premajor'),
27+
preminor: semverInc(currentVersion, 'preminor'),
28+
prepatch: semverInc(currentVersion, 'prepatch'),
29+
prerelease: semverInc(currentVersion, 'prerelease'),
30+
});
31+
32+
const timeLog = (logInfo: string, type: 'start' | 'end') => {
33+
let info = '';
34+
if (type === 'start') {
35+
info = `=> 开始任务:${logInfo}`;
36+
} else {
37+
info = `✨ 结束任务:${logInfo}`;
38+
}
39+
const nowDate = new Date();
40+
console.log(
41+
`[${nowDate.toLocaleString()}.${nowDate
42+
.getMilliseconds()
43+
.toString()
44+
.padStart(3, '0')}] ${info}
45+
`,
46+
);
47+
};
48+
49+
/**
50+
* 获取下一次版本号
51+
*/
52+
async function prompt(): Promise<string> {
53+
const nextVersions = getNextVersions();
54+
const { nextVersion } = await inquirer.prompt([
55+
{
56+
type: 'list',
57+
name: 'nextVersion',
58+
message: `请选择将要发布的版本 (当前版本 ${currentVersion})`,
59+
choices: (Object.keys(nextVersions) as Array<ReleaseType>).map(level => ({
60+
name: `${level} => ${nextVersions[level]}`,
61+
value: nextVersions[level],
62+
})),
63+
},
64+
]);
65+
return nextVersion;
66+
}
67+
68+
/**
69+
* 更新版本号
70+
* @param nextVersion 新版本号
71+
*/
72+
async function updateVersion(nextVersion: string) {
73+
pkg.version = nextVersion;
74+
timeLog('修改package.json版本号', 'start');
75+
await fs.writeFileSync(path.resolve(__dirname, './../package.json'), JSON.stringify(pkg));
76+
await run('npx prettier package.json --write');
77+
timeLog('修改package.json版本号', 'end');
78+
}
79+
80+
async function generateChangelog() {
81+
timeLog('生成CHANGELOG.md', 'start');
82+
await run(' npx conventional-changelog -p angular -i CHANGELOG.md -s -r 0');
83+
timeLog('生成CHANGELOG.md', 'end');
84+
}
85+
86+
/**
87+
* 将代码提交至git
88+
*/
89+
async function push(nextVersion: string) {
90+
timeLog('推送代码至git仓库', 'start');
91+
await run('git add package.json CHANGELOG.md');
92+
await run(`git commit -m "v${nextVersion}" -n`);
93+
await run('git push');
94+
timeLog('推送代码至git仓库', 'end');
95+
}
96+
97+
/**
98+
* 组件库打包
99+
*/
100+
async function build() {
101+
timeLog('组件库打包', 'start');
102+
await run('npm run build');
103+
timeLog('组件库打包', 'end');
104+
}
105+
106+
/**
107+
* 发布至npm
108+
*/
109+
async function publish() {
110+
timeLog('发布组件库', 'start');
111+
await run('npm publish');
112+
timeLog('发布组件库', 'end');
113+
}
114+
115+
/**
116+
* 打tag提交至git
117+
*/
118+
async function tag(nextVersion: string) {
119+
timeLog('打tag并推送至git', 'start');
120+
await run(`git tag v${nextVersion}`);
121+
await run(`git push origin tag v${nextVersion}`);
122+
timeLog('打tag并推送至git', 'end');
123+
}
124+
125+
async function main() {
126+
try {
127+
const nextVersion = await prompt();
128+
const startTime = Date.now();
129+
// =================== 更新版本号 ===================
130+
await updateVersion(nextVersion);
131+
// =================== 更新changelog ===================
132+
await generateChangelog();
133+
// =================== 代码推送git仓库 ===================
134+
await push(nextVersion);
135+
// =================== 组件库打包 ===================
136+
await build();
137+
// =================== 发布至npm ===================
138+
await publish();
139+
// =================== 打tag并推送至git ===================
140+
await tag(nextVersion);
141+
console.log(`✨ 发布流程结束 共耗时${((Date.now() - startTime) / 1000).toFixed(3)}s`);
142+
} catch (error) {
143+
console.log('💣 发布失败,失败原因:', error);
144+
}
145+
}
146+
147+
main();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/** @jsx jsx */
2+
import { useRef, useState } from 'react';
3+
import { useCurrentDoc } from 'docz';
4+
import { jsx, Layout as BaseLayout, Main } from 'theme-ui';
5+
import { Global } from '@emotion/core';
6+
7+
import global from 'gatsby-theme-docz/src/theme/global';
8+
import { Header } from 'gatsby-theme-docz/src/components/Header';
9+
import { MainContainer } from 'gatsby-theme-docz/src/components/MainContainer';
10+
import { Sidebar } from '../Sidebar';
11+
import * as styles from './styles';
12+
13+
export const Layout = ({ children }) => {
14+
const [open, setOpen] = useState(false);
15+
const nav = useRef();
16+
const current = useCurrentDoc();
17+
18+
if (current.fullpage) return children;
19+
20+
return (
21+
<BaseLayout sx={{ '& > div': { flex: '1 1 auto' } }} data-testid="layout">
22+
<Global styles={global} />
23+
<Main sx={styles.main}>
24+
<Header onOpen={() => setOpen(s => !s)} />
25+
<div sx={styles.wrapper}>
26+
<Sidebar
27+
ref={nav}
28+
open={open}
29+
onFocus={() => setOpen(true)}
30+
onBlur={() => setOpen(false)}
31+
onClick={() => setOpen(false)}
32+
/>
33+
<MainContainer data-testid="main-container">{children}</MainContainer>
34+
</div>
35+
</Main>
36+
</BaseLayout>
37+
);
38+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { media } from 'gatsby-theme-docz/src/theme/breakpoints';
2+
3+
export const main = {
4+
display: 'flex',
5+
flexDirection: 'column',
6+
minHeight: '100vh',
7+
};
8+
9+
export const wrapper = {
10+
py: 0,
11+
flex: 1,
12+
display: 'grid',
13+
gridTemplateColumns: '250px minmax(0, 1fr)',
14+
minHeight: '100vh',
15+
[media.tablet]: {
16+
display: 'block',
17+
},
18+
};
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/** @jsx jsx */
2+
import React from 'react';
3+
import { jsx } from 'theme-ui';
4+
import { Link } from 'gatsby';
5+
import { useDocs, useCurrentDoc } from 'docz';
6+
import { get } from 'lodash/fp';
7+
8+
import * as styles from './styles';
9+
10+
const getHeadings = (route, docs) => {
11+
const doc = docs.find(doc => doc.route === route);
12+
const headings = get('headings', doc);
13+
return headings ? headings.filter(heading => heading.depth === 4) : [];
14+
};
15+
16+
const getCurrentHash = () => {
17+
if (typeof window === 'undefined') {
18+
return '';
19+
}
20+
return window.location ? decodeURI(window.location.hash) : '';
21+
};
22+
23+
export const NavLink = React.forwardRef(({ item, ...props }, ref) => {
24+
const docs = useDocs();
25+
const current = useCurrentDoc();
26+
27+
if (item.hidden) {
28+
return null;
29+
}
30+
31+
const to = item.route;
32+
const headings = docs && getHeadings(to, docs);
33+
const isCurrent = item.route === current.route;
34+
const showHeadings = isCurrent && headings && headings.length > 0;
35+
const currentHash = getCurrentHash();
36+
return (
37+
<React.Fragment>
38+
<Link {...props} to={to} sx={styles.link} activeClassName="active" ref={ref} />
39+
{showHeadings &&
40+
headings.map(heading => (
41+
<Link
42+
key={heading.slug}
43+
to={`${to}#${heading.slug}`}
44+
sx={styles.smallLink}
45+
className={currentHash === `#${heading.slug}` ? 'active' : ''}
46+
>
47+
{heading.value}
48+
</Link>
49+
))}
50+
</React.Fragment>
51+
);
52+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
export const link = {
2+
my: 2,
3+
display: 'block',
4+
color: 'sidebar.navGroup',
5+
textDecoration: 'none',
6+
fontSize: 2,
7+
'&.active': {
8+
color: 'sidebar.navLinkActive',
9+
},
10+
}
11+
12+
export const smallLink = {
13+
...link,
14+
ml: 3,
15+
fontSize: 1,
16+
position: 'relative',
17+
color: 'sidebar.tocLink',
18+
'&.active': {
19+
color: 'sidebar.tocLinkActive',
20+
},
21+
'&.active::before': {
22+
content: '""',
23+
position: 'absolute',
24+
display: 'block',
25+
top: '2px',
26+
left: -2,
27+
height: '1rem',
28+
backgroundColor: 'primary',
29+
transition: 'width 200ms ease 0s',
30+
width: '2px',
31+
borderRadius: 1,
32+
},
33+
}

0 commit comments

Comments
 (0)