@@ -20,7 +20,12 @@ import { useRouter } from "next/navigation";
2020import { TreeSelect } from "antd" ;
2121import { DataNode } from "antd/es/tree" ;
2222import { buildDocsNewUrl } from "@/lib/github" ;
23- import { type DirNode , FILENAME_PATTERN } from "@/lib/submission" ;
23+ import {
24+ MAX_SLUG_LENGTH ,
25+ sanitizeSlug ,
26+ type DirNode ,
27+ validateSlug ,
28+ } from "@/lib/submission" ;
2429import {
2530 CREATE_SUBDIR_SUFFIX ,
2631 toTreeSelectData ,
@@ -60,21 +65,31 @@ export function Contribute() {
6065 const [ articleFileTouched , setArticleFileTouched ] = useState ( false ) ;
6166
6267 const trimmedArticleFile = useMemo ( ( ) => articleFile . trim ( ) , [ articleFile ] ) ;
68+ const sanitizedArticleFile = useMemo (
69+ ( ) => sanitizeSlug ( trimmedArticleFile ) ,
70+ [ trimmedArticleFile ] ,
71+ ) ;
6372 const { isFileNameValid, fileNameError } = useMemo ( ( ) => {
6473 if ( ! trimmedArticleFile ) {
6574 return {
6675 isFileNameValid : false ,
6776 fileNameError : "请填写文件名。" ,
6877 } ;
6978 }
70- if ( ! FILENAME_PATTERN . test ( trimmedArticleFile ) ) {
79+ if ( ! validateSlug ( sanitizedArticleFile ) ) {
80+ return {
81+ isFileNameValid : false ,
82+ fileNameError : `文件名仅支持英文、数字、连字符或下划线(最长 ${ MAX_SLUG_LENGTH } 个字符)。` ,
83+ } ;
84+ }
85+ if ( sanitizedArticleFile !== trimmedArticleFile ) {
7186 return {
7287 isFileNameValid : false ,
73- fileNameError : "文件名仅支持英文、数字、连字符或下划线。" ,
88+ fileNameError : `请使用规范化后的文件名: ${ sanitizedArticleFile } ` ,
7489 } ;
7590 }
7691 return { isFileNameValid : true , fileNameError : "" } ;
77- } , [ trimmedArticleFile ] ) ;
92+ } , [ sanitizedArticleFile , trimmedArticleFile ] ) ;
7893
7994 useEffect ( ( ) => {
8095 let mounted = true ;
@@ -98,22 +113,23 @@ export function Contribute() {
98113 } , [ ] ) ;
99114
100115 const options = useMemo ( ( ) => toTreeSelectData ( tree ) , [ tree ] ) ;
116+ const sanitizedSubdir = useMemo ( ( ) => sanitizeSlug ( newSub ) , [ newSub ] ) ;
101117
102118 const finalDirPath = useMemo ( ( ) => {
103119 if ( ! selectedKey ) return "" ;
104120 if ( selectedKey . endsWith ( CREATE_SUBDIR_SUFFIX ) ) {
105121 const l1 = selectedKey . split ( "/" ) [ 0 ] ;
106- if ( ! newSub . trim ( ) ) return "" ;
107- return `${ l1 } /${ newSub . trim ( ) . replace ( / \s + / g , "-" ) } ` ;
122+ if ( ! sanitizedSubdir ) return "" ;
123+ return `${ l1 } /${ sanitizedSubdir } ` ;
108124 }
109125 return selectedKey ;
110- } , [ selectedKey , newSub ] ) ;
126+ } , [ sanitizedSubdir , selectedKey ] ) ;
111127
112128 const canProceed = ! ! finalDirPath && isFileNameValid ;
113129
114130 const handleOpenGithub = ( ) => {
115131 if ( ! canProceed ) return ;
116- const filename = trimmedArticleFile . toLowerCase ( ) ;
132+ const filename = sanitizedArticleFile ;
117133 const title = articleTitle || filename ;
118134 window . open (
119135 buildGithubNewUrl ( finalDirPath , filename , title ) ,
0 commit comments