@@ -237,88 +237,110 @@ export class TCVB
237237
238238 private parse ( tcStrContent : string ) : void
239239 {
240- // 匹配文件块,每个文件块以 "## FILE:" 开头
241- const regFileBlock : RegExp = / ^ # # F I L E : ( .* ?) \n ( [ \s \S ] * ?) (? = ^ # # F I L E : | ^ # # E N D _ T C V B ) / gm;
242- let arrFileMatch : RegExpExecArray | null ;
243- while ( ( arrFileMatch = regFileBlock . exec ( tcStrContent ) ) !== null )
244- {
245- const strFilePath : string = filePathNormalize ( arrFileMatch [ 1 ] ) ;
246- const strOperationsBlock : string = arrFileMatch [ 2 ] ;
247- // 支持操作类型中含有 "-" 符号(如 exact-replace 等)
248- const regOperation : RegExp = / ^ # # O P E R A T I O N : ( [ \w - ] + ) \n ( [ \s \S ] * ?) (? = ^ # # O P E R A T I O N : | (? ! [ \s \S ] ) ) / gm;
249- let arrOpMatch : RegExpExecArray | null ;
250- while ( ( arrOpMatch = regOperation . exec ( strOperationsBlock ) ) !== null )
240+ // 从文件内容中提取 "## BEGIN_TCVB" 和 "## END_TCVB" 之间的部分
241+ const regTCVB : RegExp = / # # \s * B E G I N _ T C V B \s * ( [ \s \S ] * ?) \s * # # \s * E N D _ T C V B / ;
242+ const arrTCVBMatch : RegExpExecArray | null = regTCVB . exec ( tcStrContent ) ;
243+ if ( ! arrTCVBMatch )
251244 {
252- const strType : string = arrOpMatch [ 1 ] . toLowerCase ( ) ;
253- const strOpContent : string = arrOpMatch [ 2 ] . trim ( ) ;
254- this . parseOperation ( strFilePath , strType , strOpContent ) ;
245+ throw new Error ( "文件内容必须包含 '## BEGIN_TCVB' 和 '## END_TCVB' 之间的内容,文件不完整" ) ;
246+ }
247+ // 重新赋值 tcStrContent 为 BEGIN_TCVB 与 END_TCVB 之间的内容
248+ tcStrContent = arrTCVBMatch [ 1 ] ;
249+
250+ // 匹配文件块,每个文件块以 "## FILE:" 开头
251+ const regFileBlock : RegExp = / ^ # # F I L E : ( .* ?) \n ( [ \s \S ] * ?) (? = ^ # # F I L E : | ^ # # E N D _ T C V B ) / gm;
252+ let arrFileMatch : RegExpExecArray | null ;
253+ while ( ( arrFileMatch = regFileBlock . exec ( tcStrContent ) ) !== null )
254+ {
255+ const strFilePath : string = filePathNormalize ( arrFileMatch [ 1 ] ) ;
256+ const strOperationsBlock : string = arrFileMatch [ 2 ] ;
257+ // 支持操作类型中含有 "-" 符号(如 exact-replace 等)
258+ const regOperation : RegExp = / ^ # # O P E R A T I O N : ( [ \w - ] + ) \n ( [ \s \S ] * ?) (? = ^ # # O P E R A T I O N : | (? ! [ \s \S ] ) ) / gm;
259+ let arrOpMatch : RegExpExecArray | null ;
260+ while ( ( arrOpMatch = regOperation . exec ( strOperationsBlock ) ) !== null )
261+ {
262+ const strType : string = arrOpMatch [ 1 ] . toLowerCase ( ) ;
263+ const strOpContent : string = arrOpMatch [ 2 ] . trim ( ) ;
264+ this . parseOperation ( strFilePath , strType , strOpContent ) ;
265+ }
255266 }
256- }
257267 }
258268
259269 private parseOperation ( strFilePath : string , strType : string , strContent : string ) : void
260270 {
261- try
262- {
263271 switch ( strType )
264272 {
265- case 'global-replace' :
266- this . parseGlobalReplace ( strFilePath , strContent ) ;
267- break ;
268- case 'exact-replace' :
269- this . parseExactReplace ( strFilePath , strContent ) ;
270- break ;
271- case 'create' :
272- this . parseCreate ( strFilePath , strContent ) ;
273- break ;
274- default :
275- throw new Error ( `未知的操作类型: ${ strType } ` ) ;
273+ case 'global-replace' :
274+ this . parseGlobalReplace ( strFilePath , strContent ) ;
275+ break ;
276+ case 'exact-replace' :
277+ this . parseExactReplace ( strFilePath , strContent ) ;
278+ break ;
279+ case 'create' :
280+ this . parseCreate ( strFilePath , strContent ) ;
281+ break ;
282+ default :
283+ throw new Error ( `未知的操作类型: ${ strType } ,文件: ${ strFilePath } ` ) ;
276284 }
277- }
278- catch ( err )
279- {
280- console . error ( `解析 ${ strType } 操作时出错, 文件: ${ strFilePath } , 错误: ${ err } ` ) ;
281- }
282285 }
283286
284287 // Exact-REPLACE 操作解析:要求 BEFORE_ANCHOR、AFTER_ANCHOR、OLD_CONTENT、NEW_CONTENT 四个段落
285288 private parseExactReplace ( strFilePath : string , strContent : string ) : void
286289 {
287- const recSections = this . parseSections ( strContent , [ 'BEFORE_ANCHOR' , 'AFTER_ANCHOR' , 'OLD_CONTENT' , 'NEW_CONTENT' ] ) ;
288- this . m_arrOperations . push ( new ExactReplaceOperation (
289- strFilePath ,
290- recSections [ 'BEFORE_ANCHOR' ] ,
291- recSections [ 'AFTER_ANCHOR' ] ,
292- recSections [ 'OLD_CONTENT' ] ,
293- recSections [ 'NEW_CONTENT' ]
294- ) ) ;
290+ let recSections : { [ key : string ] : string } = { } ;
291+ try
292+ {
293+ recSections = this . parseSections ( strContent , [ 'BEFORE_ANCHOR' , 'AFTER_ANCHOR' , 'OLD_CONTENT' , 'NEW_CONTENT' ] ) ;
294+ }
295+ catch ( err : any )
296+ {
297+ throw new Error ( `解析 exact-replace 操作时,文件 "${ strFilePath } " 的内容解析失败,原因: ${ err . message } ` ) ;
298+ }
299+
300+ this . m_arrOperations . push ( new ExactReplaceOperation (
301+ strFilePath ,
302+ recSections [ 'BEFORE_ANCHOR' ] ,
303+ recSections [ 'AFTER_ANCHOR' ] ,
304+ recSections [ 'OLD_CONTENT' ] ,
305+ recSections [ 'NEW_CONTENT' ]
306+ ) ) ;
295307 }
296308
297309 // GLOBAL-REPLACE 操作解析:仅要求 OLD_CONTENT 与 NEW_CONTENT
298310 private parseGlobalReplace ( strFilePath : string , strContent : string ) : void
299311 {
300- const recSections = this . parseSections ( strContent , [ 'OLD_CONTENT' , 'NEW_CONTENT' ] ) ;
301- this . m_arrOperations . push ( new GlobalReplaceOperation (
302- strFilePath ,
303- recSections [ 'OLD_CONTENT' ] ,
304- recSections [ 'NEW_CONTENT' ]
305- ) ) ;
312+ let recSections : { [ key : string ] : string } = { } ;
313+ try
314+ {
315+ recSections = this . parseSections ( strContent , [ 'OLD_CONTENT' , 'NEW_CONTENT' ] ) ;
316+ }
317+ catch ( err : any )
318+ {
319+ throw new Error ( `解析 global-replace 操作时,文件 "${ strFilePath } " 的内容解析失败,原因: ${ err . message } ` ) ;
320+ }
321+
322+ this . m_arrOperations . push ( new GlobalReplaceOperation (
323+ strFilePath ,
324+ recSections [ 'OLD_CONTENT' ] ,
325+ recSections [ 'NEW_CONTENT' ]
326+ ) ) ;
306327 }
307328
308329 // CREATE 操作解析:直接将正文内容作为新文件内容,可选地去除 Markdown 代码块
309330 private parseCreate ( strFilePath : string , strContent : string ) : void
310331 {
311- let strNewContent : string = strContent ;
312- const regCodeBlock : RegExp = / ^ ` ` ` .* \n ( [ \s \S ] * ?) \n ` ` ` $ / m;
313- const arrMatch = regCodeBlock . exec ( strNewContent ) ;
314- if ( arrMatch )
315- {
316- strNewContent = arrMatch [ 1 ] ;
317- }
318- this . m_arrOperations . push ( new CreateOperation (
319- strFilePath ,
320- strNewContent
321- ) ) ;
332+ let strNewContent : string = strContent ;
333+ const regCodeBlock : RegExp = / ^ ` ` ` .* \n ( [ \s \S ] * ?) \n ` ` ` $ / m;
334+ const arrMatch : RegExpExecArray | null = regCodeBlock . exec ( strNewContent ) ;
335+ if ( arrMatch )
336+ {
337+ strNewContent = arrMatch [ 1 ] ;
338+ }
339+
340+ this . m_arrOperations . push ( new CreateOperation (
341+ strFilePath ,
342+ strNewContent
343+ ) ) ;
322344 }
323345
324346 // 辅助方法:剥离 Markdown 代码块外部包裹的 ``` 标记
@@ -345,8 +367,8 @@ export class TCVB
345367 return strTrimmedContent ;
346368 }
347369
348- // 辅助方法:解析操作正文中的各个段落(段落标记格式为 "## 段落名称")
349- private parseSections ( strContent : string , arrExpectedSections : string [ ] ) : Record < string , string >
370+ // 辅助方法:解析操作正文中的各个段落(段落标记格式为 "## 段落名称")
371+ private parseSections ( strContent : string , arrExpectedSections : string [ ] ) : Record < string , string >
350372 {
351373 const recResult : Record < string , string > = { } ;
352374 let strCurrentSection : string | null = null ;
@@ -370,7 +392,11 @@ export class TCVB
370392
371393 if ( arrExpectedSections . indexOf ( strCurrentSection ) === - 1 )
372394 {
373- throw new Error ( `意外的段落: ${ strCurrentSection } ` ) ;
395+ const cMaxLen : number = 50 ;
396+ const strSnippet : string = strContent . length <= cMaxLen
397+ ? strContent
398+ : strContent . substring ( 0 , cMaxLen ) + '...' ;
399+ throw new Error ( `意外的段落: ${ strCurrentSection } ,操作原始内容部分为: ${ strSnippet } ` ) ;
374400 }
375401 }
376402 else if ( strCurrentSection )
@@ -388,9 +414,13 @@ export class TCVB
388414 // 检查是否缺少必需的段落
389415 for ( const strSection of arrExpectedSections )
390416 {
391- if ( ! ( strSection in recResult ) )
417+ if ( ! ( strSection in recResult ) )
392418 {
393- throw new Error ( `缺失必需的段落: ${ strSection } ` ) ;
419+ const cMaxLen : number = 50 ;
420+ const strSnippet : string = strContent . length <= cMaxLen
421+ ? strContent
422+ : strContent . substring ( 0 , cMaxLen ) + '...' ;
423+ throw new Error ( `缺失必需的段落: ${ strSection } ,操作原始内容部分为: ${ strSnippet } ` ) ;
394424 }
395425 }
396426
@@ -493,7 +523,7 @@ export function mergeCvb(baseCvb: Cvb, tcvb: TCVB) : Cvb
493523 else if ( op instanceof CreateOperation )
494524 {
495525 if ( mapMergedFiles . has ( strFilePath ) ) {
496- throw new Error ( `${ strFilePath } 已经存在,不可以使用 ## OPERATION:CREATE` )
526+ throw new Error ( `${ strFilePath } 已经存在,不可以使用 ## OPERATION:CREATE` ) ;
497527 }
498528 // CREATE 操作:直接以新内容覆盖原有内容
499529 strContent = op . m_strContent ;
0 commit comments