(url, mdxPath)
| 114 | } |
| 115 | |
| 116 | function isValidRef(url, mdxPath) { |
| 117 | if (isExternal(url)) return { ok: true }; |
| 118 | const target = stripFragment(url); |
| 119 | if (!target) return { ok: true }; |
| 120 | |
| 121 | // 绝对路径 |
| 122 | if (target.startsWith('/')) { |
| 123 | if (isSkippedAttachment(target)) return { ok: true }; |
| 124 | if (target.startsWith('/imgs/')) { |
| 125 | const p = path.join(PUBLIC_DIR, target); |
| 126 | return fileExists(p) ? { ok: true } : { ok: false, expected: p }; |
| 127 | } |
| 128 | const ext = path.extname(target).toLowerCase(); |
| 129 | if (ext && !MDX_EXTS.includes(ext)) { |
| 130 | const p = path.join(PUBLIC_DIR, target); |
| 131 | return fileExists(p) ? { ok: true } : { ok: false, expected: p }; |
| 132 | } |
| 133 | const langMatch = target.match(/^\/(en|zh)\/(.+)$/); |
| 134 | const subRaw = langMatch ? langMatch[2] : target.slice(1); |
| 135 | const sub = normalizeRoutePath(subRaw); |
| 136 | const base = path.join(CONTENT_DIR, sub); |
| 137 | return routeExists(base) |
| 138 | ? { ok: true } |
| 139 | : { ok: false, expected: `${base}(.mdx | .en.mdx | /index.mdx)` }; |
| 140 | } |
| 141 | // 相对路径 |
| 142 | const ext = path.extname(target).toLowerCase(); |
| 143 | if (ext && !MDX_EXTS.includes(ext)) { |
| 144 | const resolved = path.resolve(path.dirname(mdxPath), target); |
| 145 | if (FILE_LIKE_EXTS.has(ext)) { |
| 146 | return fileExists(resolved) ? { ok: true } : { ok: false, expected: resolved }; |
| 147 | } |
| 148 | return fileExists(resolved) ? { ok: true } : { ok: false, expected: resolved }; |
| 149 | } |
| 150 | const normalized = normalizeRoutePath(target); |
| 151 | const resolved = path.resolve(path.dirname(mdxPath), normalized); |
| 152 | return routeExists(resolved) |
| 153 | ? { ok: true } |
| 154 | : { ok: false, expected: `${resolved}(.mdx | .en.mdx | /index.mdx)` }; |
| 155 | } |
| 156 | |
| 157 | function isMetaFile(name) { |
| 158 | return /^meta(\.en)?\.json$/.test(name); |
no test coverage detected