Ver código fonte

fix(i18n): encode exact link paths safely

pku-xht 2 semanas atrás
pai
commit
ae9b69287f
2 arquivos alterados com 12 adições e 6 exclusões
  1. 5 5
      scripts/translation-links.spec.ts
  2. 7 1
      scripts/translation-links.ts

+ 5 - 5
scripts/translation-links.spec.ts

@@ -27,8 +27,8 @@ function fixture(): string {
   writeFileSync(join(root, 'docs/guide.zh.md'), '# 指南\n')
   writeFileSync(join(root, 'docs/reference.md'), '# Overview\n')
   writeFileSync(join(root, 'docs/reference.zh.md'), '# 概览\n')
-  writeFileSync(join(root, 'docs/a#b.md'), '# Reserved\n')
-  writeFileSync(join(root, 'docs/a#b.zh.md'), '# 保留字符\n')
+  writeFileSync(join(root, 'docs/a)#?b.md'), '# Reserved\n')
+  writeFileSync(join(root, 'docs/a)#?b.zh.md'), '# 保留字符\n')
   writeFileSync(join(root, 'docs/unpaired.md'), '# Only\n')
   writeFileSync(join(root, 'docs/section/index.md'), '# Section\n')
   writeFileSync(join(root, 'docs/section/index.zh.md'), '# 章节\n')
@@ -87,11 +87,11 @@ describe('translation link locale validation', () => {
     })
   })
 
-  it('keeps URL-reserved filename bytes escaped in an encoded exact path', () => {
+  it('encodes each exact path segment with only RFC 3986 unreserved characters', () => {
     const root = fixture()
-    const input = '[保留](a%23b%2Emd?view=full#section)\n'
+    const input = '[保留](a%29%23%3Fb%2Emd?view=full#section)\n'
     expect(rewriteTranslationLinkLocales(input, linkContext(root, 'docs/guide.zh.md'))).toEqual({
-      content: '[保留](a%23b.zh.md?view=full#section)\n',
+      content: '[保留](a%29%23%3Fb.zh.md?view=full#section)\n',
       rewritten: 1,
     })
   })

+ 7 - 1
scripts/translation-links.ts

@@ -143,13 +143,19 @@ function translationPairTarget(targetPath: string, context: TranslationLinkConte
   return { source, zh }
 }
 
+function encodePathSegment(segment: string): string {
+  return encodeURIComponent(segment).replace(/[!'()*]/g, character => (
+    `%${character.charCodeAt(0).toString(16).toUpperCase()}`
+  ))
+}
+
 function relativeExpectedPath(
   context: TranslationLinkContext,
   expectedPath: string,
   rawPath: string,
 ): string {
   const relative = posix.relative(posix.dirname(context.sourcePath), expectedPath)
-  const encoded = relative.split('/').map(segment => encodeURIComponent(segment)).join('/')
+  const encoded = relative.split('/').map(encodePathSegment).join('/')
   return rawPath.startsWith('./') && !encoded.startsWith('.') ? `./${encoded}` : encoded
 }