From 910c3665864294abcebc3330256549951feb1986 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Fri, 17 Mar 2023 11:34:49 +0800 Subject: [PATCH 1/9] docs: update CONTRIBUTING.md for preview url (#469) Co-authored-by: Qingyu Deng --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c29dc61a3..00f5e40d0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,6 +76,7 @@ git push -u origin - PR 的标题需要满足 [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) 规范的要求 - 尚未完成的 PR 请在标题中添加 `[WIP]`,或者设置为 draft 状态。 +- ci 会对 PR 进行构建并进行部署,如果涉及 UI 修改,在预览上传成功后请修改 PR 说明添加预览 URL。 ### 在合并之前 From 52c5590c7bad1161a12dd1964d28ca7d4223036c Mon Sep 17 00:00:00 2001 From: Jan30chen <36024707+Jan30chen@users.noreply.github.com> Date: Fri, 17 Mar 2023 12:12:48 +0800 Subject: [PATCH 2/9] feat: error toast (#435) Co-authored-by: Trim21 --- .../components/Message/Message.stories.tsx | 69 +++++++++++++++++++ .../Message/__test__/Message.spec.tsx | 21 ++++++ .../__snapshots__/Message.spec.tsx.snap | 52 ++++++++++++++ packages/design/components/Message/index.tsx | 41 +++++++++++ .../components/Message/style/index.less | 37 ++++++++++ .../design/components/Message/style/index.tsx | 1 + .../design/components/Toast/Toast.stories.tsx | 40 +++++++++-- packages/design/components/Toast/Toast.tsx | 5 +- .../components/Toast/__test__/Toast.spec.tsx | 2 +- packages/design/components/Toast/index.tsx | 9 ++- .../design/components/Toast/style/index.less | 7 +- packages/design/components/Toast/types.ts | 3 + packages/design/index.tsx | 2 + .../pages/login => icons}/assets/error.svg | 0 packages/icons/index.tsx | 2 +- .../components/ErrorMessage/index.module.less | 17 ----- .../login/components/ErrorMessage/index.tsx | 19 ----- packages/website/src/pages/login/index.tsx | 9 ++- 18 files changed, 279 insertions(+), 57 deletions(-) create mode 100644 packages/design/components/Message/Message.stories.tsx create mode 100644 packages/design/components/Message/__test__/Message.spec.tsx create mode 100644 packages/design/components/Message/__test__/__snapshots__/Message.spec.tsx.snap create mode 100644 packages/design/components/Message/index.tsx create mode 100644 packages/design/components/Message/style/index.less create mode 100644 packages/design/components/Message/style/index.tsx rename packages/{website/src/pages/login => icons}/assets/error.svg (100%) delete mode 100644 packages/website/src/pages/login/components/ErrorMessage/index.module.less delete mode 100644 packages/website/src/pages/login/components/ErrorMessage/index.tsx diff --git a/packages/design/components/Message/Message.stories.tsx b/packages/design/components/Message/Message.stories.tsx new file mode 100644 index 000000000..351954104 --- /dev/null +++ b/packages/design/components/Message/Message.stories.tsx @@ -0,0 +1,69 @@ +import type { ComponentMeta, ComponentStory } from '@storybook/react'; +import React from 'react'; + +import Message from '.'; + +const componentMeta: ComponentMeta = { + title: 'modern/Message', + component: Message, + parameters: { + docs: { + description: { + component: '消息提示块,可以直接用于页面中,也用于Toast组件中。', + }, + }, + }, + argTypes: { + children: { + description: '消息内容', + defaultValue: '这是一条消息提示', + control: { type: 'text' }, + table: { + type: { summary: 'string' }, + }, + }, + type: { + description: '消息类型:对应不同样式', + control: { type: 'select' }, + options: ['info', 'error'], + table: { + type: { summary: 'string' }, + defaultValue: { summary: 'info' }, + }, + defaultValue: 'info', + }, + blockWidth: { + description: '消息长度:是否占据整行', + control: { type: 'inline-radio' }, + options: [true, false], + table: { + type: { summary: 'boolean' }, + defaultValue: { summary: false }, + }, + defaultValue: false, + }, + }, + decorators: [ + (story) => ( +
+ {story()} +
+ ), + ], +}; +export default componentMeta; + +const Template: ComponentStory = (args) => { + return ( + <> + + + ); +}; + +export const Usage = Template.bind({}); diff --git a/packages/design/components/Message/__test__/Message.spec.tsx b/packages/design/components/Message/__test__/Message.spec.tsx new file mode 100644 index 000000000..3c28a712f --- /dev/null +++ b/packages/design/components/Message/__test__/Message.spec.tsx @@ -0,0 +1,21 @@ +import { render } from '@testing-library/react'; +import React from 'react'; + +import Message from '..'; + +describe('', () => { + it('should be render a Message', () => { + const { container } = render(example-message); + expect(container).toMatchSnapshot(); + }); + + it('should be a whole line', () => { + const { container } = render(example-message-block); + expect(container).toMatchSnapshot(); + }); + + it('should be a error message', () => { + const { container } = render(example-message-error); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/packages/design/components/Message/__test__/__snapshots__/Message.spec.tsx.snap b/packages/design/components/Message/__test__/__snapshots__/Message.spec.tsx.snap new file mode 100644 index 000000000..9bbf0db0b --- /dev/null +++ b/packages/design/components/Message/__test__/__snapshots__/Message.spec.tsx.snap @@ -0,0 +1,52 @@ +// Vitest Snapshot v1 + +exports[` > should be a error message 1`] = ` +
+
+
+
+ + example-message-error + +
+
+
+`; + +exports[` > should be a whole line 1`] = ` +
+
+ +
+`; + +exports[` > should be render a Message 1`] = ` +
+
+ +
+`; diff --git a/packages/design/components/Message/index.tsx b/packages/design/components/Message/index.tsx new file mode 100644 index 000000000..9b37d863d --- /dev/null +++ b/packages/design/components/Message/index.tsx @@ -0,0 +1,41 @@ +import './style'; + +import classnames from 'classnames'; +import React from 'react'; + +import { Error } from '@bangumi/icons'; + +export type MessageType = 'info' | 'error'; +export interface MessageProps { + /** 消息类型:对应不同样式 */ + type?: MessageType; + /** 消息长度:是否占据整行 */ + blockWidth?: boolean; + /** 自定义类名 */ + className?: string; +} + +const Message = ({ + type = 'info', + blockWidth = false, + className, + children, +}: React.PropsWithChildren) => { + const classes = classnames( + 'bgm-message__content', + `bgm-message__content--${type}`, + { 'bgm-message__content--block': blockWidth }, + className, + ); + + return ( +
+
+ + {children} +
+
+ ); +}; + +export default Message; diff --git a/packages/design/components/Message/style/index.less b/packages/design/components/Message/style/index.less new file mode 100644 index 000000000..e8b8d5980 --- /dev/null +++ b/packages/design/components/Message/style/index.less @@ -0,0 +1,37 @@ +@import '../../../theme/base'; + +.bgm-message { + display: flex; + + &__content { + border-radius: 19px; + padding: 9px 16px; + box-sizing: border-box; + display: flex; + align-items: center; + font-weight: 600; + font-size: 14px; + line-height: 20px; + + > svg { + flex: none; + width: 20px; + height: 20px; + margin-right: 8px; + } + } + + &__content--block { + flex: 1; + } + + &__content--info { + background-color: rgba(90, 87, 87, 0.6); + color: #fff; + } + + &__content--error { + background-color: #f97f77; + color: #fff; + } +} diff --git a/packages/design/components/Message/style/index.tsx b/packages/design/components/Message/style/index.tsx new file mode 100644 index 000000000..d74e52ee9 --- /dev/null +++ b/packages/design/components/Message/style/index.tsx @@ -0,0 +1 @@ +import './index.less'; diff --git a/packages/design/components/Toast/Toast.stories.tsx b/packages/design/components/Toast/Toast.stories.tsx index 44f088a6b..359506a14 100644 --- a/packages/design/components/Toast/Toast.stories.tsx +++ b/packages/design/components/Toast/Toast.stories.tsx @@ -11,13 +11,39 @@ const componentMeta: ComponentMeta = { export default componentMeta; export const Demo = () => { + const message = '提示消息'; + const longMessage = ` + 哀民生之多艰。余虽好修姱以鞿羁兮, + 謇朝谇而夕替。既替余以蕙纕兮,又申之以揽茝。 + 亦余心之所善兮,虽九死其犹未悔。 + 怨灵修之浩荡兮,终不察夫民心。 + 众女嫉余之蛾眉兮,谣诼谓余以善淫。 + 固时俗之工巧兮,偭规矩而改错。 + 背绳墨以追曲兮,竞周容以为度。 + 忳郁邑余侘傺兮,吾独穷困乎此时也。`; return ( - + <> + + + + ); }; diff --git a/packages/design/components/Toast/Toast.tsx b/packages/design/components/Toast/Toast.tsx index 6f82d4665..57c421cba 100644 --- a/packages/design/components/Toast/Toast.tsx +++ b/packages/design/components/Toast/Toast.tsx @@ -4,6 +4,7 @@ import classnames from 'classnames'; import { delay } from 'lodash'; import React from 'react'; +import Message from '../Message'; import type { Toast as TToast } from './types'; import { removeToastEvent } from './utils/event-bus'; @@ -16,7 +17,7 @@ const DEFAULT_TOAST_TIMEOUT = 5000; const FADE_OUT_TIME = 300; export const Toast: React.FC = ({ toast }) => { - const { message, timeout = DEFAULT_TOAST_TIMEOUT } = toast; + const { message, timeout = DEFAULT_TOAST_TIMEOUT, type } = toast; const [isVisible, setIsVisible] = React.useState(false); React.useEffect(() => { @@ -39,7 +40,7 @@ export const Toast: React.FC = ({ toast }) => { 'bgm-toast--visible': isVisible, })} > - {message} + {message}
); }; diff --git a/packages/design/components/Toast/__test__/Toast.spec.tsx b/packages/design/components/Toast/__test__/Toast.spec.tsx index f0d15ba09..8ffe0b764 100644 --- a/packages/design/components/Toast/__test__/Toast.spec.tsx +++ b/packages/design/components/Toast/__test__/Toast.spec.tsx @@ -21,7 +21,7 @@ afterEach(() => { }); it('should show message and disappear after given time', () => { - const toast: TToast = { message: 'test', tid: '1', timeout: 1000 }; + const toast: TToast = { message: 'test', tid: '1', type: 'info', timeout: 1000 }; const { getByText } = render(); act(() => { diff --git a/packages/design/components/Toast/index.tsx b/packages/design/components/Toast/index.tsx index 0a00018a0..d4bde6e53 100644 --- a/packages/design/components/Toast/index.tsx +++ b/packages/design/components/Toast/index.tsx @@ -4,6 +4,7 @@ import { defer, uniqueId } from 'lodash'; import React from 'react'; import ReactDOM from 'react-dom/client'; +import type { MessageType } from '../Message'; import { ToastContainer } from './ToastContainer'; import { insertToastEvent } from './utils/event-bus'; @@ -12,6 +13,7 @@ export { Toast } from './Toast'; const TOAST_CONTAINER_CLS_NAME = 'bgm-toast__container'; interface ToastOptions { + type?: MessageType; timeout?: number; } @@ -29,6 +31,11 @@ export function toast(message: string, options: ToastOptions = {}) { } defer(() => { - insertToastEvent.emit({ message, tid: uniqueId(), timeout: options.timeout }); + insertToastEvent.emit({ + message, + tid: uniqueId(), + type: options.type, + timeout: options.timeout, + }); }); } diff --git a/packages/design/components/Toast/style/index.less b/packages/design/components/Toast/style/index.less index 599514de5..e4c96f6a4 100644 --- a/packages/design/components/Toast/style/index.less +++ b/packages/design/components/Toast/style/index.less @@ -13,15 +13,10 @@ } .bgm-toast { - border-radius: 16px; - padding: 18px; - color: #fff; - background: rgba(90, 87, 87, 0.6); - font-size: 16px; - line-height: 22px; transition: opacity 0.3s ease-in-out; opacity: 0; margin-bottom: 5px; + max-width: 600px; &--visible { opacity: 1; diff --git a/packages/design/components/Toast/types.ts b/packages/design/components/Toast/types.ts index db71d6de5..6d355fbd1 100644 --- a/packages/design/components/Toast/types.ts +++ b/packages/design/components/Toast/types.ts @@ -1,5 +1,8 @@ +import type { MessageType } from '../Message'; + export interface Toast { message: string; tid: string; + type?: MessageType; timeout?: number; } diff --git a/packages/design/index.tsx b/packages/design/index.tsx index ac36078d5..a302434fe 100644 --- a/packages/design/index.tsx +++ b/packages/design/index.tsx @@ -17,6 +17,7 @@ export { default as Layout } from './components/Layout'; export { default as Form } from './components/Form'; export { default as Select } from './components/Select'; export { default as Radio } from './components/Radio'; +export { default as Message } from './components/Message'; export { toast } from './components/Toast'; export type { ButtonProps } from './components/Button'; @@ -33,3 +34,4 @@ export type { EditorFormProps } from './components/EditorForm'; export type { RichContentProps } from './components/RichContent'; export type { PaginationProps } from './components/Pagination'; export type { LayoutProps } from './components/Layout'; +export type { MessageType } from './components/Message'; diff --git a/packages/website/src/pages/login/assets/error.svg b/packages/icons/assets/error.svg similarity index 100% rename from packages/website/src/pages/login/assets/error.svg rename to packages/icons/assets/error.svg diff --git a/packages/icons/index.tsx b/packages/icons/index.tsx index 2d738d324..82d2f438b 100644 --- a/packages/icons/index.tsx +++ b/packages/icons/index.tsx @@ -18,7 +18,7 @@ export { ReactComponent as ArrowRightCircle } from './assets/arrow-right-circle. export { ReactComponent as Plus } from './assets/plus.svg'; export { ReactComponent as Minus } from './assets/minus.svg'; export { ReactComponent as Cursor } from './assets/cursor.svg'; - +export { ReactComponent as Error } from './assets/error.svg'; // BBCode Editor export { ReactComponent as Bold } from './assets/bold.svg'; diff --git a/packages/website/src/pages/login/components/ErrorMessage/index.module.less b/packages/website/src/pages/login/components/ErrorMessage/index.module.less deleted file mode 100644 index 2a7cbc5ef..000000000 --- a/packages/website/src/pages/login/components/ErrorMessage/index.module.less +++ /dev/null @@ -1,17 +0,0 @@ -.container { - background-color: #f97f77; - border-radius: 19px; - font-weight: 600; - font-size: 14px; - line-height: 20px; - color: #fff; - padding: 9px 16px; - width: 100%; - box-sizing: border-box; - display: flex; - align-items: center; - - > * { - margin-right: 8px; - } -} diff --git a/packages/website/src/pages/login/components/ErrorMessage/index.tsx b/packages/website/src/pages/login/components/ErrorMessage/index.tsx deleted file mode 100644 index 6044e7411..000000000 --- a/packages/website/src/pages/login/components/ErrorMessage/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; - -import { ReactComponent as Error } from '../../assets/error.svg'; -import style from './index.module.less'; - -interface ErrorMessageProps { - message: string; -} - -const ErrorMessage: React.FC = ({ message }) => { - return ( -
- - {message} -
- ); -}; - -export default ErrorMessage; diff --git a/packages/website/src/pages/login/index.tsx b/packages/website/src/pages/login/index.tsx index 391ac5817..932f201e5 100644 --- a/packages/website/src/pages/login/index.tsx +++ b/packages/website/src/pages/login/index.tsx @@ -4,7 +4,7 @@ import React, { useRef } from 'react'; import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; import { useInput } from 'rooks'; -import { Button, Input } from '@bangumi/design'; +import { Button, Input, Message } from '@bangumi/design'; import { Password, UserLogin } from '@bangumi/icons'; import { @@ -15,7 +15,6 @@ import { useUser, } from '../../hooks/use-user'; import { ReactComponent as LoginLogo } from './assets/login-logo.svg'; -import ErrorMessage from './components/ErrorMessage'; import style from './index.module.less'; const Login: React.FC = () => { @@ -108,7 +107,11 @@ const Login: React.FC = () => {
- {errorMessage && } + {errorMessage && ( + + {errorMessage} + + )} } placeholder='你的 Email 地址' {...email} /> } placeholder='你的登录密码' {...password} />
From 646521574b26423ffc5724852dc3ac7a9872bf30 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Fri, 17 Mar 2023 23:53:14 +0800 Subject: [PATCH 3/9] feat: add request id in global error page (#466) --- .../components/ErrorBoundary/ErrorLayout.tsx | 10 ++++- .../src/components/ErrorBoundary/index.tsx | 37 +++++++++++++------ .../ErrorBoundary/style.module.less | 5 +++ .../src/pages/index/group/topic/[id].tsx | 10 +---- packages/website/vite.config.ts | 7 ++++ 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/website/src/components/ErrorBoundary/ErrorLayout.tsx b/packages/website/src/components/ErrorBoundary/ErrorLayout.tsx index 26021aeea..2292f37f1 100644 --- a/packages/website/src/components/ErrorBoundary/ErrorLayout.tsx +++ b/packages/website/src/components/ErrorBoundary/ErrorLayout.tsx @@ -7,7 +7,10 @@ import { PureLink } from '@bangumi/design/components/Typography/Link'; import layoutStyle from './style.module.less'; -export default function ErrorLayout({ children }: PropsWithChildren<{}>) { +export default function ErrorLayout({ + children, + requestID, +}: PropsWithChildren<{ requestID?: string | null }>) { const navigate = useNavigate(); return (
@@ -25,6 +28,11 @@ export default function ErrorLayout({ children }: PropsWithChildren<{}>) { 返回上页
+ {requestID && ( +
+
request-id: {requestID}
+
+ )}
); diff --git a/packages/website/src/components/ErrorBoundary/index.tsx b/packages/website/src/components/ErrorBoundary/index.tsx index 2bf55a4fc..8e38efd32 100644 --- a/packages/website/src/components/ErrorBoundary/index.tsx +++ b/packages/website/src/components/ErrorBoundary/index.tsx @@ -2,14 +2,29 @@ import { HttpError } from 'oazapfts'; import type { PropsWithChildren } from 'react'; import React from 'react'; +import type { Error as resError } from '@bangumi/client/client'; + import ErrorLayout from './ErrorLayout'; type CatchError = HttpError | Error | null; -type ErrorBoundaryFallbackFC = Record JSX.Element) | JSX.Element>; + +/** + * 响应的 error code 的优先级高于 http status code。 + * + * @example + * ```tsx + * { + * 'NOT_ALLOWED_ERROR': . + * 404: , + * } + * ``` + */ +type ErrorBoundaryFallbackFC = Record JSX.Element) | JSX.Element>; interface ErrorBoundaryState { error: CatchError; } + const initialState: ErrorBoundaryState = { error: null }; // Error boundaries currently have to be classes. @@ -31,23 +46,21 @@ export default class ErrorBoundary extends React.Component< if (error) { let fb: ((err: CatchError) => JSX.Element) | JSX.Element | undefined; let msg = error.message ?? '发生未知错误'; + let reqID: string | null = null; if (error instanceof HttpError) { - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/no-unsafe-member-access - if (error.data?.message) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - msg = error.data.message; - } - if (fallback) { - // 选择对应 status code 的 fallback - fb = fallback[error.status]; - } + reqID = error.headers.get('cf-ray'); + const { message = msg, code = error.status } = (error.data ?? {}) as Partial; + msg = message; + // 选择对应 statusCode / err code 的 fallback + fb = fallback?.[code]; } return ( - - {fb ? (typeof fb === 'function' ? fb(this.state.error) : fb) : msg} + + {typeof fb === 'function' ? fb(error) : fb ?? msg} ); } + return this.props.children; } } diff --git a/packages/website/src/components/ErrorBoundary/style.module.less b/packages/website/src/components/ErrorBoundary/style.module.less index 6cb827b77..e8f83be7a 100644 --- a/packages/website/src/components/ErrorBoundary/style.module.less +++ b/packages/website/src/components/ErrorBoundary/style.module.less @@ -34,6 +34,11 @@ color: @gray-80; } + .info { + color: @gray-80; + font-size: 14px; + } + .footer { font-weight: 600; color: @gray-60; diff --git a/packages/website/src/pages/index/group/topic/[id].tsx b/packages/website/src/pages/index/group/topic/[id].tsx index 83635eabb..722f8733d 100644 --- a/packages/website/src/pages/index/group/topic/[id].tsx +++ b/packages/website/src/pages/index/group/topic/[id].tsx @@ -1,12 +1,6 @@ import React from 'react'; import { Outlet } from 'react-router-dom'; -import ErrorBoundary from '@bangumi/website/components/ErrorBoundary'; +import { withErrorBoundary } from '@bangumi/website/components/ErrorBoundary'; -const GroupTopicPage = () => ( - Topic Not found }}> - - -); - -export default GroupTopicPage; +export default withErrorBoundary(Outlet, { 404: <>Topic Not found }); diff --git a/packages/website/vite.config.ts b/packages/website/vite.config.ts index bd571ac67..9c6489269 100644 --- a/packages/website/vite.config.ts +++ b/packages/website/vite.config.ts @@ -1,3 +1,4 @@ +import * as crypto from 'node:crypto'; import path from 'node:path'; import react from '@vitejs/plugin-react'; @@ -61,6 +62,12 @@ export default defineConfig(({ mode }) => { proxyReq.setHeader('Referer', apiDomain + '/'); }); proxy.on('proxyRes', (proxyRes) => { + const h = proxyRes.headers['cf-ray'] ?? ''; + if (h === '') { + proxyRes.headers['cf-ray'] = ('fake-' + crypto.randomUUID()).slice(0, 20); + } else if (Array.isArray(h)) { + proxyRes.headers['cf-ray'] = h[0] ?? ('fake-' + crypto.randomUUID()).slice(0, 20); + } // 本地开发环境没有 https 带有 secure attribute 的 set-cookies 无效, // 所以在本地开发时移除 secure attribute const setCookies = proxyRes.headers['set-cookie']; From 7a3d865a51036217d442c62c6391678937f90644 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Mar 2023 03:11:38 +0800 Subject: [PATCH 4/9] build(deps): update dependency stylelint-config-standard to v31 (#476) --- package.json | 5 +- .../design/components/Toast/style/index.less | 5 +- pnpm-lock.yaml | 94 +++++++++---------- 3 files changed, 51 insertions(+), 53 deletions(-) diff --git a/package.json b/package.json index ad683cb83..2026799f4 100644 --- a/package.json +++ b/package.json @@ -87,9 +87,10 @@ "prettier": "^2.8.4", "react": "^18.2.0", "react-dom": "^18.2.0", - "stylelint": "^15.2.0", + "stylelint": "^15.3.0", "stylelint-config-css-modules": "^4.2.0", - "stylelint-config-standard": "^30.0.1", + "stylelint-config-standard": "^31.0.0", + "stylelint-scss": "^4.5.0", "timezone-mock": "^1.3.6", "typescript": "^4.9.5", "vite": "^4.2.0", diff --git a/packages/design/components/Toast/style/index.less b/packages/design/components/Toast/style/index.less index e4c96f6a4..83d0fbd4d 100644 --- a/packages/design/components/Toast/style/index.less +++ b/packages/design/components/Toast/style/index.less @@ -1,9 +1,6 @@ .bgm-toast__container { position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; + inset: 0; display: flex; flex-direction: column; justify-content: center; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 83d4aee54..e93f7e87d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,9 +41,10 @@ importers: prettier: ^2.8.4 react: ^18.2.0 react-dom: ^18.2.0 - stylelint: ^15.2.0 + stylelint: ^15.3.0 stylelint-config-css-modules: ^4.2.0 - stylelint-config-standard: ^30.0.1 + stylelint-config-standard: ^31.0.0 + stylelint-scss: ^4.5.0 timezone-mock: ^1.3.6 typescript: ^4.9.5 vite: ^4.2.0 @@ -89,9 +90,10 @@ importers: prettier: 2.8.4 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - stylelint: 15.2.0 - stylelint-config-css-modules: 4.2.0_stylelint@15.2.0 - stylelint-config-standard: 30.0.1_stylelint@15.2.0 + stylelint: 15.3.0 + stylelint-config-css-modules: 4.2.0_stylelint@15.3.0 + stylelint-config-standard: 31.0.0_stylelint@15.3.0 + stylelint-scss: 4.5.0_stylelint@15.3.0 timezone-mock: 1.3.6 typescript: 4.9.5 vite: 4.2.0_@types+node@18.15.3 @@ -3256,29 +3258,29 @@ packages: dev: true optional: true - /@csstools/css-parser-algorithms/2.0.1_xu4ijqbgbw3jz65fprqzqcck3y: + /@csstools/css-parser-algorithms/2.0.1_5vzy4lghjvuzkedkkk4tqwjftm: resolution: {integrity: sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==} engines: {node: ^14 || ^16 || >=18} peerDependencies: '@csstools/css-tokenizer': ^2.0.0 dependencies: - '@csstools/css-tokenizer': 2.0.2 + '@csstools/css-tokenizer': 2.1.0 dev: true - /@csstools/css-tokenizer/2.0.2: - resolution: {integrity: sha512-prUTipz0NZH7Lc5wyBUy93NFy3QYDMVEQgSeZzNdpMbKRd6V2bgRFyJ+O0S0Dw0MXWuE/H9WXlJk3kzMZRHZ/g==} + /@csstools/css-tokenizer/2.1.0: + resolution: {integrity: sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==} engines: {node: ^14 || ^16 || >=18} dev: true - /@csstools/media-query-list-parser/2.0.1_3cwabixgovb7jwtbsdb6ktsak4: + /@csstools/media-query-list-parser/2.0.1_ppok7cytzjc65mcyxmtit3wdyi: resolution: {integrity: sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==} engines: {node: ^14 || ^16 || >=18} peerDependencies: '@csstools/css-parser-algorithms': ^2.0.0 '@csstools/css-tokenizer': ^2.0.0 dependencies: - '@csstools/css-parser-algorithms': 2.0.1_xu4ijqbgbw3jz65fprqzqcck3y - '@csstools/css-tokenizer': 2.0.2 + '@csstools/css-parser-algorithms': 2.0.1_5vzy4lghjvuzkedkkk4tqwjftm + '@csstools/css-tokenizer': 2.1.0 dev: true /@csstools/selector-specificity/2.1.1_wajs5nedgkikc5pcuwett7legi: @@ -6014,7 +6016,7 @@ packages: '@babel/plugin-transform-react-jsx-source': 7.19.6_@babel+core@7.21.0 magic-string: 0.27.0 react-refresh: 0.14.0 - vite: 4.2.0_less@4.1.3 + vite: 4.2.0_@types+node@18.15.3 transitivePeerDependencies: - supports-color dev: true @@ -8004,8 +8006,8 @@ packages: yaml: 1.10.2 dev: true - /cosmiconfig/8.0.0: - resolution: {integrity: sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==} + /cosmiconfig/8.1.2: + resolution: {integrity: sha512-rmpUFKMZiawLfug8sP4NbpBSOpWftZB6UACOLEiNbnRAYM1TzgQuTWlMYFRuPgmoTCkcOxSMwQJQpJmiXv/eHw==} engines: {node: '>=14'} dependencies: import-fresh: 3.3.0 @@ -11508,8 +11510,8 @@ packages: engines: {node: '>= 8'} dev: true - /known-css-properties/0.26.0: - resolution: {integrity: sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==} + /known-css-properties/0.27.0: + resolution: {integrity: sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==} dev: true /lazy-universal-dotenv/3.0.1: @@ -14996,60 +14998,58 @@ packages: inline-style-parser: 0.1.1 dev: true - /stylelint-config-css-modules/4.2.0_stylelint@15.2.0: + /stylelint-config-css-modules/4.2.0_stylelint@15.3.0: resolution: {integrity: sha512-5x7lzPNCc42puQEAFdr7dSzQ00aIg1vCVyV+QPUiSp2oZILpAt8HTgveXaDttazxcwWPBNJrxrLpa556xUP7Bw==} peerDependencies: stylelint: ^14.5.1 || ^15.0.0 dependencies: - stylelint: 15.2.0 + stylelint: 15.3.0 optionalDependencies: - stylelint-scss: 4.3.0_stylelint@15.2.0 + stylelint-scss: 4.5.0_stylelint@15.3.0 dev: true - /stylelint-config-recommended/10.0.1_stylelint@15.2.0: - resolution: {integrity: sha512-TQ4xQ48tW4QSlODcti7pgSRqBZcUaBzuh0jPpfiMhwJKBPkqzTIAU+IrSWL/7BgXlOM90DjB7YaNgFpx8QWhuA==} + /stylelint-config-recommended/11.0.0_stylelint@15.3.0: + resolution: {integrity: sha512-SoGIHNI748OCZn6BxFYT83ytWoYETCINVHV3LKScVAWQQauWdvmdDqJC5YXWjpBbxg2E761Tg5aUGKLFOVhEkA==} peerDependencies: - stylelint: ^15.0.0 + stylelint: ^15.3.0 dependencies: - stylelint: 15.2.0 + stylelint: 15.3.0 dev: true - /stylelint-config-standard/30.0.1_stylelint@15.2.0: - resolution: {integrity: sha512-NbeHOmpRQhjZh5XB1B/S4MLRWvz4xxAxeDBjzl0tY2xEcayNhLbaRGF0ZQzq+DQZLCcPpOHeS2Ru1ydbkhkmLg==} + /stylelint-config-standard/31.0.0_stylelint@15.3.0: + resolution: {integrity: sha512-CUGAmtROCvX0YgMY2+6P9tqSkHj5z/75XxrQ8bGxvkCa1xYdGDx4poM0pa7cXc3s74/PZLJH/okxZZouRfOSGw==} peerDependencies: - stylelint: ^15.0.0 + stylelint: ^15.3.0 dependencies: - stylelint: 15.2.0 - stylelint-config-recommended: 10.0.1_stylelint@15.2.0 + stylelint: 15.3.0 + stylelint-config-recommended: 11.0.0_stylelint@15.3.0 dev: true - /stylelint-scss/4.3.0_stylelint@15.2.0: - resolution: {integrity: sha512-GvSaKCA3tipzZHoz+nNO7S02ZqOsdBzMiCx9poSmLlb3tdJlGddEX/8QzCOD8O7GQan9bjsvLMsO5xiw6IhhIQ==} - requiresBuild: true + /stylelint-scss/4.5.0_stylelint@15.3.0: + resolution: {integrity: sha512-/+rQ8FePOiwT5xblOHkujYzRYfSjmE6HYhLpqJShL+9wH6/HaAVj4mWpXlpEsM3ZgIpOblG9Y+/BycSJzWgjNw==} peerDependencies: - stylelint: ^14.5.1 + stylelint: ^14.5.1 || ^15.0.0 dependencies: lodash: 4.17.21 postcss-media-query-parser: 0.2.3 postcss-resolve-nested-selector: 0.1.1 postcss-selector-parser: 6.0.11 postcss-value-parser: 4.2.0 - stylelint: 15.2.0 + stylelint: 15.3.0 dev: true - optional: true - /stylelint/15.2.0: - resolution: {integrity: sha512-wjg5OLn8zQwjlj5cYUgyQpMWKzct42AG5dYlqkHRJQJqsystFFn3onqEc263KH4xfEI0W3lZCnlIhFfS64uwSA==} + /stylelint/15.3.0: + resolution: {integrity: sha512-9UYBYk7K9rtlKcTUDZrtntE840sZM00qyYBQHHe7tjwMNUsPsGvR6Fd43IxHEAhRrDLzpy3TVaHb6CReBB3eFg==} engines: {node: ^14.13.1 || >=16.0.0} hasBin: true dependencies: - '@csstools/css-parser-algorithms': 2.0.1_xu4ijqbgbw3jz65fprqzqcck3y - '@csstools/css-tokenizer': 2.0.2 - '@csstools/media-query-list-parser': 2.0.1_3cwabixgovb7jwtbsdb6ktsak4 + '@csstools/css-parser-algorithms': 2.0.1_5vzy4lghjvuzkedkkk4tqwjftm + '@csstools/css-tokenizer': 2.1.0 + '@csstools/media-query-list-parser': 2.0.1_ppok7cytzjc65mcyxmtit3wdyi '@csstools/selector-specificity': 2.1.1_wajs5nedgkikc5pcuwett7legi balanced-match: 2.0.0 colord: 2.9.3 - cosmiconfig: 8.0.0 + cosmiconfig: 8.1.2 css-functions-list: 3.1.0 css-tree: 2.3.1 debug: 4.3.4 @@ -15064,7 +15064,7 @@ packages: import-lazy: 4.0.0 imurmurhash: 0.1.4 is-plain-object: 5.0.0 - known-css-properties: 0.26.0 + known-css-properties: 0.27.0 mathml-tag-names: 2.1.3 meow: 9.0.0 micromatch: 4.0.5 @@ -15080,7 +15080,7 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 style-search: 0.1.0 - supports-hyperlinks: 2.3.0 + supports-hyperlinks: 3.0.0 svg-tags: 1.0.0 table: 6.8.1 v8-compile-cache: 2.3.0 @@ -15115,9 +15115,9 @@ packages: engines: {node: '>=12'} dev: true - /supports-hyperlinks/2.3.0: - resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} - engines: {node: '>=8'} + /supports-hyperlinks/3.0.0: + resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} + engines: {node: '>=14.18'} dependencies: has-flag: 4.0.0 supports-color: 7.2.0 @@ -16062,7 +16062,7 @@ packages: dependencies: '@rollup/pluginutils': 5.0.2 '@svgr/core': 6.5.1 - vite: 4.2.0_less@4.1.3 + vite: 4.2.0_@types+node@18.15.3 transitivePeerDependencies: - rollup - supports-color From 51953e8be0543580d6460f23d559b7cddb826400 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Sat, 18 Mar 2023 06:11:54 +0800 Subject: [PATCH 5/9] refactor: update generated client (#477) --- packages/client/api.yaml | 97 ++----------------- packages/client/client.ts | 38 +------- packages/client/types/index.ts | 65 +------------ packages/website/src/hooks/use-user.spec.tsx | 2 +- packages/website/src/hooks/use-user.tsx | 2 +- .../website/src/pages/login/index.spec.tsx | 2 +- 6 files changed, 17 insertions(+), 189 deletions(-) diff --git a/packages/client/api.yaml b/packages/client/api.yaml index fdb92535f..18f6a0990 100644 --- a/packages/client/api.yaml +++ b/packages/client/api.yaml @@ -314,6 +314,8 @@ components: type: description: 查看 `./lib/notify.ts` _settings type: integer + unread: + type: boolean required: - id - title @@ -322,6 +324,7 @@ components: - topicID - postID - createdAt + - unread type: object Permission: properties: @@ -1375,95 +1378,6 @@ paths: description: 意料之外的服务器错误 tags: - auth - /p1/login2: - post: - deprecated: true - description: 'backward compatibility for #login operator' - operationId: login2 - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LoginRequestBody' - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/User' - description: Default Response - headers: - Set-Cookie: - schema: - type: string - '400': - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: request validation error - description: request validation error - '401': - content: - application/json: - examples: - CAPTCHA_ERROR: - value: - code: CAPTCHA_ERROR - error: Unauthorized - message: wrong captcha - statusCode: 401 - EMAIL_PASSWORD_ERROR: - value: - code: EMAIL_PASSWORD_ERROR - error: Unauthorized - message: email does not exists or email and password not match - statusCode: 401 - schema: - $ref: '#/components/schemas/Error' - description: 验证码错误/账号密码不匹配 - description: 验证码错误/账号密码不匹配 - headers: - X-RateLimit-Limit: - schema: - type: integer - X-RateLimit-Remaining: - schema: - type: integer - X-RateLimit-Reset: - schema: - type: integer - '429': - content: - application/json: - example: - code: TOO_MANY_REQUESTS - error: Too Many Requests - message: too many failed login attempts - statusCode: 429 - schema: - $ref: '#/components/schemas/Error' - description: 失败次数太多,需要过一段时间再重试 - description: 失败次数太多,需要过一段时间再重试 - headers: - X-RateLimit-Limit: - schema: - type: integer - X-RateLimit-Remaining: - schema: - type: integer - X-RateLimit-Reset: - schema: - type: integer - '500': - content: - application/json: - schema: - $ref: '#/components/schemas/Error' - description: 意料之外的服务器错误 - description: 意料之外的服务器错误 - tags: - - auth /p1/logout: post: description: 登出 @@ -1547,6 +1461,11 @@ paths: schema: default: 20 type: integer + - in: query + name: unread + required: false + schema: + type: boolean responses: '200': content: diff --git a/packages/client/client.ts b/packages/client/client.ts index 0d2530d37..6cadf47de 100644 --- a/packages/client/client.ts +++ b/packages/client/client.ts @@ -163,6 +163,7 @@ export interface Notice { title: string; topicID: number; type: number; + unread: boolean; } export interface WikiPlatform { id: number; @@ -567,40 +568,6 @@ export async function login(loginRequestBody?: LoginRequestBody, opts?: Oazapfts }), ); } -/** - * backward compatibility for #login operator - */ -export async function login2(loginRequestBody?: LoginRequestBody, opts?: Oazapfts.RequestOpts) { - return oazapfts.fetchJson< - | { - status: 200; - data: User; - } - | { - status: 400; - data: Error; - } - | { - status: 401; - data: Error; - } - | { - status: 429; - data: Error; - } - | { - status: 500; - data: Error; - } - >( - '/p1/login2', - oazapfts.json({ - ...opts, - method: 'POST', - body: loginRequestBody, - }), - ); -} /** * 登出 */ @@ -651,8 +618,10 @@ export async function getCurrentUser(opts?: Oazapfts.RequestOpts) { export async function listNotice( { limit, + unread, }: { limit?: number; + unread?: boolean; } = {}, opts?: Oazapfts.RequestOpts, ) { @@ -676,6 +645,7 @@ export async function listNotice( `/p1/notify${QS.query( QS.explode({ limit, + unread, }), )}`, { diff --git a/packages/client/types/index.ts b/packages/client/types/index.ts index 56691a1ed..71612e87f 100644 --- a/packages/client/types/index.ts +++ b/packages/client/types/index.ts @@ -51,13 +51,6 @@ export interface paths { */ post: operations['login']; }; - '/p1/login2': { - /** - * @deprecated - * @description backward compatibility for #login operator - */ - post: operations['login2']; - }; '/p1/logout': { /** @description 登出 */ post: operations['logout']; @@ -233,6 +226,7 @@ export interface components { topicID: number; /** @description 查看 `./lib/notify.ts` _settings */ type: number; + unread: boolean; }; Permission: { subjectWikiEdit: boolean; @@ -786,62 +780,6 @@ export interface operations { }; }; }; - /** - * @deprecated - * @description backward compatibility for #login operator - */ - login2: { - requestBody?: { - content: { - 'application/json': components['schemas']['LoginRequestBody']; - }; - }; - responses: { - /** @description Default Response */ - 200: { - headers: { - 'Set-Cookie'?: string; - }; - content: { - 'application/json': components['schemas']['User']; - }; - }; - /** @description request validation error */ - 400: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description 验证码错误/账号密码不匹配 */ - 401: { - headers: { - 'X-RateLimit-Limit'?: number; - 'X-RateLimit-Remaining'?: number; - 'X-RateLimit-Reset'?: number; - }; - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description 失败次数太多,需要过一段时间再重试 */ - 429: { - headers: { - 'X-RateLimit-Limit'?: number; - 'X-RateLimit-Remaining'?: number; - 'X-RateLimit-Reset'?: number; - }; - content: { - 'application/json': components['schemas']['Error']; - }; - }; - /** @description 意料之外的服务器错误 */ - 500: { - content: { - 'application/json': components['schemas']['Error']; - }; - }; - }; - }; /** @description 登出 */ logout: { requestBody?: { @@ -897,6 +835,7 @@ export interface operations { parameters: { query: { limit?: number; + unread?: boolean; }; }; responses: { diff --git a/packages/website/src/hooks/use-user.spec.tsx b/packages/website/src/hooks/use-user.spec.tsx index c46701cb1..06b97a468 100644 --- a/packages/website/src/hooks/use-user.spec.tsx +++ b/packages/website/src/hooks/use-user.spec.tsx @@ -20,7 +20,7 @@ function mockLogin( headers: Record = {}, ): void { mockServer.use( - rest.post('http://localhost:3000/p1/login2', async (req, res, ctx) => { + rest.post('http://localhost:3000/p1/login', async (req, res, ctx) => { return res(ctx.status(statusCode), ctx.set(headers), ctx.json(response)); }), ); diff --git a/packages/website/src/hooks/use-user.tsx b/packages/website/src/hooks/use-user.tsx index 421ba5774..31574f47b 100644 --- a/packages/website/src/hooks/use-user.tsx +++ b/packages/website/src/hooks/use-user.tsx @@ -74,7 +74,7 @@ export const useUser: () => UserContextType = () => { }; async function login(email: string, password: string, cfCaptchaResponse: string): Promise { - const res = await ozaClient.login2({ + const res = await ozaClient.login({ email, password, 'cf-turnstile-response': cfCaptchaResponse, diff --git a/packages/website/src/pages/login/index.spec.tsx b/packages/website/src/pages/login/index.spec.tsx index 5241a392d..0bf5804a9 100644 --- a/packages/website/src/pages/login/index.spec.tsx +++ b/packages/website/src/pages/login/index.spec.tsx @@ -35,7 +35,7 @@ function mockLogin( headers: Record = {}, ): void { mockServer.use( - rest.post('http://localhost:3000/p1/login2', async (req, res, ctx) => { + rest.post('http://localhost:3000/p1/login', async (req, res, ctx) => { return res(ctx.status(statusCode), ctx.set(headers), ctx.json(response)); }), ); From 2114edc23de513ecad559096f0f2b6115df281b1 Mon Sep 17 00:00:00 2001 From: bangumi-bot <124712095+bangumi-bot@users.noreply.github.com> Date: Sat, 18 Mar 2023 06:27:05 +0800 Subject: [PATCH 6/9] chore(types): update private API type definition (#478) --- packages/client/api.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/api.yaml b/packages/client/api.yaml index 18f6a0990..57b3fd8de 100644 --- a/packages/client/api.yaml +++ b/packages/client/api.yaml @@ -664,7 +664,7 @@ components: type: object securitySchemes: CookiesSession: - description: 使用 [login](#/auth/login2) 调用 API 登录,或者 使用 [demo](/demo/login) 登录 + description: 使用 [login](#/auth/login) 调用 API 登录,或者 使用 [demo](/demo/login) 登录 in: cookie name: chiiNextSessionID type: apiKey From 237f8359247b92ba18c36bc3a48b9e70e883a922 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Sun, 19 Mar 2023 12:23:40 +0800 Subject: [PATCH 7/9] ci: update and create new next milestone when release (#481) --- .github/scripts/milestone.mts | 89 ++++++++++++++++++++++++++++++++++ .github/workflows/e2e-test.yml | 2 + .github/workflows/release.yml | 11 +++++ package.json | 7 ++- pnpm-lock.yaml | 46 ++++++++++++++++++ tsconfig.json | 1 + 6 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 .github/scripts/milestone.mts diff --git a/.github/scripts/milestone.mts b/.github/scripts/milestone.mts new file mode 100644 index 000000000..76de2f385 --- /dev/null +++ b/.github/scripts/milestone.mts @@ -0,0 +1,89 @@ +import * as core from '@actions/core'; +import * as github from '@actions/github'; +import type { components } from '@octokit/openapi-types'; + +import packageJSON from '../../package.json'; + +type Milestone = components['schemas']['nullable-milestone']; + +const { version } = packageJSON; + +const repo = { + owner: 'bangumi', + repo: 'frontend', +}; + +async function main() { + const githubToken = process.env.GH_TOKEN; + if (!githubToken) { + throw new Error('process.env.GH_TOKEN is empty'); + } + + const octokit = github.getOctokit(githubToken); + + const milestones = await octokit.paginate('GET /repos/{owner}/{repo}/milestones', { ...repo }); + + let oldNextMilestone: undefined | Milestone; + + for (const milestone of milestones) { + if (milestone.title === 'next') { + oldNextMilestone = milestone; + break; + } + } + + if (!oldNextMilestone) { + core.info('missing previous `next` milestone, skipping script'); + return; + } + + const openIssues = await octokit.paginate('GET /repos/{owner}/{repo}/issues', { + ...repo, + state: 'open', + milestone: oldNextMilestone.number.toString(), + }); + + core.info(`close and rename old milestone ${oldNextMilestone.number}`); + await octokit.request('PATCH /repos/{owner}/{repo}/milestones/{milestone_number}', { + ...repo, + milestone_number: oldNextMilestone.number, + title: version, + state: 'closed', + description: + '# script is still moving issues, DO NOT EDIT IT NOW\n' + + (oldNextMilestone.description ?? ''), + }); + + core.info(`create new next milestone`); + const newNextMileStone = await octokit.request('POST /repos/{owner}/{repo}/milestones', { + ...repo, + title: 'next', + description: '# script is still moving issues, DO NOT EDIT IT NOW', + }); + + for (const issue of openIssues) { + core.info(`moving issue ${issue.number} to new milestone ${newNextMileStone.data.number}`); + await octokit.request('PATCH /repos/{owner}/{repo}/issues/{issue_number}', { + ...repo, + issue_number: issue.number, + milestone: newNextMileStone.data.number, + }); + } + + core.info(`restore issue description`); + await octokit.request('PATCH /repos/{owner}/{repo}/milestones/{milestone_number}', { + ...repo, + milestone_number: oldNextMilestone.number, + description: oldNextMilestone.description ?? '', + }); + + core.info(`update new issue next title`); + await octokit.request('PATCH /repos/{owner}/{repo}/milestones/{milestone_number}', { + ...repo, + milestone_number: newNextMileStone.data.number, + title: 'next', + description: 'milestone for next release', + }); +} + +await main(); diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 7337f3488..7203fd545 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -5,10 +5,12 @@ on: branches: [master] paths-ignore: - '**.md' + - '.github/scripts/**' pull_request: branches: [master] paths-ignore: - '**.md' + - '.github/scripts/**' jobs: test: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d15135718..5eaaa5765 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,3 +25,14 @@ jobs: run: gh release create "$TAG" bangumi-website.tar.gz bangumi-website.tar.gz.sha256 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + milestone: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/setup-js-env + + - name: Update github milestone + run: npx tsx .github/scripts/milestone.mts + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 2026799f4..e1996b2c9 100644 --- a/package.json +++ b/package.json @@ -41,20 +41,22 @@ "trailingComma": "all" }, "lint-staged": { - "*.{js,ts,tsx,cjs,mjs}": [ + "*.{js,ts,mts,cts,tsx,cjs,mjs}": [ "eslint --fix" ], "*.{css,less}": [ "stylelint --fix" ], - "*.{md,html,js,ts,tsx,css,less,cjs,mjs,yaml,yml,json}": "prettier --write" + "*.{md,html,js,ts,mts,cts,tsx,css,less,cjs,mjs,yaml,yml,json}": "prettier --write" }, "devDependencies": { + "@actions/core": "^1.10.0", "@actions/exec": "^1.1.1", "@actions/github": "^5.1.1", "@babel/core": "^7.21.0", "@babel/preset-env": "^7.20.2", "@babel/preset-typescript": "^7.21.0", + "@octokit/openapi-types": "^16.0.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^14.0.0", "@types/node": "^18.15.3", @@ -92,6 +94,7 @@ "stylelint-config-standard": "^31.0.0", "stylelint-scss": "^4.5.0", "timezone-mock": "^1.3.6", + "tsx": "^3.12.5", "typescript": "^4.9.5", "vite": "^4.2.0", "vite-plugin-svgr": "^2.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e93f7e87d..67c4890c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,11 +4,13 @@ importers: .: specifiers: + '@actions/core': ^1.10.0 '@actions/exec': ^1.1.1 '@actions/github': ^5.1.1 '@babel/core': ^7.21.0 '@babel/preset-env': ^7.20.2 '@babel/preset-typescript': ^7.21.0 + '@octokit/openapi-types': ^16.0.0 '@testing-library/jest-dom': ^5.16.5 '@testing-library/react': ^14.0.0 '@types/node': ^18.15.3 @@ -46,6 +48,7 @@ importers: stylelint-config-standard: ^31.0.0 stylelint-scss: ^4.5.0 timezone-mock: ^1.3.6 + tsx: ^3.12.5 typescript: ^4.9.5 vite: ^4.2.0 vite-plugin-svgr: ^2.4.0 @@ -53,11 +56,13 @@ importers: vitest-github-actions-reporter: ^0.10.0 whatwg-fetch: ^3.6.2 devDependencies: + '@actions/core': 1.10.0 '@actions/exec': 1.1.1 '@actions/github': 5.1.1 '@babel/core': 7.21.0 '@babel/preset-env': 7.20.2_@babel+core@7.21.0 '@babel/preset-typescript': 7.21.0_@babel+core@7.21.0 + '@octokit/openapi-types': 16.0.0 '@testing-library/jest-dom': 5.16.5 '@testing-library/react': 14.0.0_biqbaboplfbrettd7655fr4n2y '@types/node': 18.15.3 @@ -95,6 +100,7 @@ importers: stylelint-config-standard: 31.0.0_stylelint@15.3.0 stylelint-scss: 4.5.0_stylelint@15.3.0 timezone-mock: 1.3.6 + tsx: 3.12.5 typescript: 4.9.5 vite: 4.2.0_@types+node@18.15.3 vite-plugin-svgr: 2.4.0_vite@4.2.0 @@ -3299,6 +3305,27 @@ packages: engines: {node: '>=10.0.0'} dev: true + /@esbuild-kit/cjs-loader/2.4.2: + resolution: {integrity: sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==} + dependencies: + '@esbuild-kit/core-utils': 3.1.0 + get-tsconfig: 4.4.0 + dev: true + + /@esbuild-kit/core-utils/3.1.0: + resolution: {integrity: sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==} + dependencies: + esbuild: 0.17.11 + source-map-support: 0.5.21 + dev: true + + /@esbuild-kit/esm-loader/2.5.5: + resolution: {integrity: sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==} + dependencies: + '@esbuild-kit/core-utils': 3.1.0 + get-tsconfig: 4.4.0 + dev: true + /@esbuild/android-arm/0.17.11: resolution: {integrity: sha512-CdyX6sRVh1NzFCsf5vw3kULwlAhfy9wVt8SZlrhQ7eL2qBjGbFhRBWkkAzuZm9IIEOCKJw4DXA6R85g+qc8RDw==} engines: {node: '>=12'} @@ -3902,6 +3929,10 @@ packages: resolution: {integrity: sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==} dev: true + /@octokit/openapi-types/16.0.0: + resolution: {integrity: sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA==} + dev: true + /@octokit/plugin-paginate-rest/2.21.3_@octokit+core@3.6.0: resolution: {integrity: sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==} peerDependencies: @@ -10018,6 +10049,10 @@ packages: resolution: {integrity: sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==} dev: true + /get-tsconfig/4.4.0: + resolution: {integrity: sha512-0Gdjo/9+FzsYhXCEFueo2aY1z1tpXrxWZzP7k8ul9qt1U5o8rYJwTJYmaeHdrVosYIVYkOy2iwCJ9FdpocJhPQ==} + dev: true + /get-value/2.0.6: resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} engines: {node: '>=0.10.0'} @@ -15550,6 +15585,17 @@ packages: typescript: 4.9.5 dev: true + /tsx/3.12.5: + resolution: {integrity: sha512-/TLj30xF1zcN9JkoFCyROtIQUi8cRQG+AFchsg5YkWou3+RXxTZS/ffWB3nCxyZPoBqF2+8ohs07N815dNb1wQ==} + hasBin: true + dependencies: + '@esbuild-kit/cjs-loader': 2.4.2 + '@esbuild-kit/core-utils': 3.1.0 + '@esbuild-kit/esm-loader': 2.5.5 + optionalDependencies: + fsevents: 2.3.2 + dev: true + /tty-browserify/0.0.0: resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} dev: true diff --git a/tsconfig.json b/tsconfig.json index 58bca2fb2..6603e50b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,6 +31,7 @@ "./tests/", ".eslintrc.js", "vitest.config.ts", + ".github/scripts/*", "./packages/design/.storybook/*" ] } From 28d28fb18b648f9325133532b819299834948f74 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Sun, 19 Mar 2023 12:50:31 +0800 Subject: [PATCH 8/9] ci: rewrite scripts with ts (#482) --- .eslintrc.js | 2 +- ...{upload-preview.cjs => upload-preview.mts} | 90 +++++++++---------- .github/workflows/preview.yaml | 2 +- package.json | 2 +- 4 files changed, 48 insertions(+), 48 deletions(-) rename .github/scripts/{upload-preview.cjs => upload-preview.mts} (57%) diff --git a/.eslintrc.js b/.eslintrc.js index 20e3f87f2..ac811de43 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -45,7 +45,7 @@ module.exports = { }, overrides: [ { - files: ['*.ts', '*.tsx'], + files: ['*.ts', '*.mts', '*.cts', '*.tsx'], extends: [ 'standard-with-typescript', 'standard-jsx', diff --git a/.github/scripts/upload-preview.cjs b/.github/scripts/upload-preview.mts similarity index 57% rename from .github/scripts/upload-preview.cjs rename to .github/scripts/upload-preview.mts index 526fc4f32..f6160189e 100644 --- a/.github/scripts/upload-preview.cjs +++ b/.github/scripts/upload-preview.mts @@ -1,11 +1,16 @@ -const fs = require('node:fs'); -const path = require('node:path'); +/* eslint-disable @typescript-eslint/no-use-before-define */ -const { exec } = require('@actions/exec'); -const github = require('@actions/github'); -const { context } = require('@actions/github'); +import * as fs from 'node:fs'; +import * as path from 'node:path'; -const artifacts = { +import { exec } from '@actions/exec'; +import { context } from '@actions/github'; +import * as github from '@actions/github'; +import type { GitHub } from '@actions/github/lib/utils'; + +type Client = InstanceType; + +const artifacts: Record = { Build: 'sites', 'Storybook Build': 'storybook', }; @@ -24,12 +29,11 @@ async function main() { throw new Error('process.env.workflow_name is empty'); } - if (!Object.keys(artifacts).includes(workflowName)) { + const artifact = artifacts[workflowName]; + if (!artifact) { throw new Error(`not valid workflow name ${workflowName}`); } - const artifact = artifacts[workflowName]; - await exec( 'gh', [ @@ -37,7 +41,7 @@ async function main() { `${github.context.repo.owner}/${github.context.repo.repo}`, 'run', 'download', - `${process.env.RUN_ID}`, + `${context.runId}`, '--name', artifact, '--dir', @@ -45,7 +49,7 @@ async function main() { ], { env: { - GH_TOKEN: process.env.GH_TOKEN, + GH_TOKEN: process.env.GH_TOKEN ?? '', }, }, ); @@ -56,37 +60,44 @@ async function main() { await exec('netlify', ['deploy', `--dir=${artifact}`, `--alias="${alias}"`], { env: { - NETLIFY_AUTH_TOKEN: process.env.NETLIFY_AUTH_TOKEN, - NETLIFY_SITE_ID: process.env.NETLIFY_SITE_ID, - PATH: process.env.PATH, + NETLIFY_AUTH_TOKEN: process.env.NETLIFY_AUTH_TOKEN ?? '', + NETLIFY_SITE_ID: process.env.NETLIFY_SITE_ID ?? '', + PATH: process.env.PATH ?? '', }, }); - for await (const { data: comments } of octokit.paginate.iterator( - octokit.rest.issues.listComments, + const comments = await octokit.paginate( + 'GET /repos/{owner}/{repo}/issues/{issue_number}/comments', { owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, }, - )) { - for (const comment of comments) { - if (comment.user.login === 'github-actions[bot]' && comment.body.includes(commentComment)) { - return await updateComment(octokit, comment, artifact, alias); - } + ); + + for (const comment of comments) { + if (comment.user?.login === 'github-actions[bot]' && comment.body?.includes(commentComment)) { + return updateComment( + octokit, + { + id: comment.id, + body: comment.body ?? '', + }, + artifact, + alias, + ); } } await createComment(octokit, prNumber, artifact, alias); } -/** - * @param {InstanceType} octokit - * @param {string} artifact - * @param {string} alias - * @param {{id:number;body:string;}} comment - */ -async function updateComment(octokit, comment, artifact, alias) { +async function updateComment( + octokit: Client, + comment: { id: number; body: string }, + artifact: string, + alias: string, +) { const links = []; const s = comment.body.split('\n').filter(Boolean); @@ -104,7 +115,7 @@ async function updateComment(octokit, comment, artifact, alias) { links.unshift(commentComment, '# Preview Deployment'); - await octokit.rest.issues.updateComment({ + await octokit.request('POST /repos/{owner}/{repo}/issues/comments', { owner: context.repo.owner, repo: context.repo.repo, comment_id: comment.id, @@ -112,14 +123,8 @@ async function updateComment(octokit, comment, artifact, alias) { }); } -/** - * @param {InstanceType} octokit - * @param {number} prNumber - * @param {string} artifact - * @param {string} alias - */ -async function createComment(octokit, prNumber, artifact, alias) { - await octokit.rest.issues.createComment({ +async function createComment(octokit: Client, prNumber: number, artifact: string, alias: string) { + await octokit.request('POST /repos/{owner}/{repo}/issues/comments', { owner: context.repo.owner, repo: context.repo.repo, issue_number: prNumber, @@ -131,17 +136,12 @@ async function createComment(octokit, prNumber, artifact, alias) { }); } -/** - * @param {string} s - */ -function toTitle(s) { +function toTitle(s: string) { if (!s) { return ''; } - return s[0].toUpperCase() + s.slice(1).toLowerCase(); + return (s[0]?.toUpperCase() ?? '') + s.slice(1).toLowerCase(); } -main().catch((e) => { - throw e; -}); +await main(); diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml index 815425e04..36b08a9e6 100644 --- a/.github/workflows/preview.yaml +++ b/.github/workflows/preview.yaml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@v3 - uses: ./.github/actions/setup-js-env - - run: node ./.github/scripts/upload-preview.cjs + - run: npx tsx ./.github/scripts/upload-preview.mts env: workflow_name: '${{ github.event.workflow.name }}' GH_TOKEN: ${{ github.token }} diff --git a/package.json b/package.json index e1996b2c9..2d0d6e995 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "dev": "pnpm dev:csr", "dev:csr": "pnpm website dev", "dev:ssr": "pnpm server dev", - "lint": "eslint ./ .github/scripts --ext cjs,mjs,js,jsx,ts,tsx", + "lint": "eslint ./ .github/scripts --ext cjs,mjs,js,jsx,ts,mts,cts,tsx", "lint:fix": "pnpm lint --fix", "lint:style": "stylelint \"./packages/**/*.{css,less}\"", "lint:style-fix": "pnpm lint:style --fix", From babc8ebe9b096354ad65f1d3821fb55b6c428a15 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Sun, 19 Mar 2023 12:58:14 +0800 Subject: [PATCH 9/9] ci: fix preview uploading script --- .github/scripts/upload-preview.mts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/scripts/upload-preview.mts b/.github/scripts/upload-preview.mts index f6160189e..2a3669289 100644 --- a/.github/scripts/upload-preview.mts +++ b/.github/scripts/upload-preview.mts @@ -34,6 +34,11 @@ async function main() { throw new Error(`not valid workflow name ${workflowName}`); } + const RUN_ID = process.env.RUN_ID; + if (!RUN_ID) { + throw new Error('process.env.RUN_ID is empty'); + } + await exec( 'gh', [ @@ -41,7 +46,7 @@ async function main() { `${github.context.repo.owner}/${github.context.repo.repo}`, 'run', 'download', - `${context.runId}`, + RUN_ID, '--name', artifact, '--dir',