|
1 | 1 | import fs from 'fs/promises';
|
2 | 2 | import path from 'path';
|
3 |
| -import { exec } from 'child_process'; |
4 | 3 |
|
5 | 4 | const CONTENT_DIR = path.join(process.cwd(), 'public/content');
|
6 | 5 | const REDIRECTS_JSON_PATH = path.join(CONTENT_DIR, 'redirects.json');
|
7 | 6 | const ALIAS_JSON_PATH = path.join(CONTENT_DIR, 'aliases.json');
|
8 | 7 |
|
9 |
| -function removingTrailingSlash(path: string): string { |
| 8 | +export function removingTrailingSlash(path: string): string { |
10 | 9 | return path.replace(/\/$/, '');
|
11 | 10 | }
|
12 | 11 |
|
@@ -35,20 +34,6 @@ export async function getJSONFileContent(
|
35 | 34 | }
|
36 | 35 | }
|
37 | 36 |
|
38 |
| -export async function execPromise(command: string): Promise<void> { |
39 |
| - return new Promise((resolve, reject) => { |
40 |
| - const child = exec(command, { cwd: process.cwd() }, (error) => { |
41 |
| - if (error) { |
42 |
| - reject(error); |
43 |
| - } else { |
44 |
| - resolve(); |
45 |
| - } |
46 |
| - }); |
47 |
| - child.stdout?.pipe(process.stdout); |
48 |
| - child.stderr?.pipe(process.stderr); |
49 |
| - }); |
50 |
| -} |
51 |
| - |
52 | 37 | /**
|
53 | 38 | * Gets all markdown and MDX files recursively from a directory
|
54 | 39 | * @param dir The directory to search in
|
@@ -174,51 +159,127 @@ export async function getAliasPaths(): Promise<Record<string, string>> {
|
174 | 159 | return Object.assign({}, nestedAliasPaths, aliases);
|
175 | 160 | }
|
176 | 161 |
|
177 |
| -function getIsSelfReferential(entryURL: string, targetURL: string): boolean { |
178 |
| - // Check if the entry URL is a self-referential alias |
179 |
| - return normalizePathWithSlash(entryURL) === normalizePathWithSlash(targetURL); |
180 |
| -} |
181 |
| - |
182 |
| -function getNestedRelatedAliases( |
183 |
| - aliases: Record<string, string>, |
184 |
| -): Record<string, string> { |
185 |
| - const nestedAliases: Record<string, string> = {}; |
186 |
| - for (const [aliasKey, aliasValue] of Object.entries(aliases)) { |
187 |
| - // Split the alias key into segments |
188 |
| - const segments = aliasKey.split('/').filter(Boolean); |
189 |
| - // Create a new key for each segment |
190 |
| - for (let i = 1; i <= segments.length; i++) { |
191 |
| - const newKey = segments.slice(i).join('/'); |
192 |
| - if (!newKey) { |
193 |
| - continue; // Skip empty keys |
194 |
| - } |
195 |
| - const isSelfReferential = getIsSelfReferential(newKey, aliasValue); |
196 |
| - if (isSelfReferential) { |
197 |
| - break; |
198 |
| - } |
199 |
| - if (!nestedAliases[newKey]) { |
200 |
| - nestedAliases[newKey] = aliasValue; |
201 |
| - } |
202 |
| - } |
203 |
| - // Add the original alias if it doesn't already exist |
204 |
| - if (!nestedAliases[aliasKey]) { |
205 |
| - nestedAliases[aliasKey] = aliasValue; |
206 |
| - } |
207 |
| - } |
208 |
| - return nestedAliases; |
209 |
| -} |
210 |
| - |
211 | 162 | export async function getReversedAliasPaths(): Promise<Record<string, string>> {
|
212 | 163 | const aliases = await getAliasPaths();
|
213 | 164 |
|
214 | 165 | // Reverse the keys and values
|
215 | 166 | const reversedAliases = Object.fromEntries(
|
216 | 167 | Object.entries(aliases).map(([key, value]) => [value, key]),
|
217 | 168 | );
|
218 |
| - return getNestedRelatedAliases(reversedAliases); |
| 169 | + return reversedAliases; |
219 | 170 | }
|
220 | 171 |
|
221 | 172 | export async function getRedirects(): Promise<Record<string, string>> {
|
222 | 173 | const redirects = await getJSONFileContent(REDIRECTS_JSON_PATH);
|
223 | 174 | return redirects;
|
224 | 175 | }
|
| 176 | + |
| 177 | +async function filterRedirectsLogic( |
| 178 | + processRedirect: ( |
| 179 | + normalizedRedirectKey: string, |
| 180 | + normalizedRedirectValue: string, |
| 181 | + aliasesEntries: [string, string][], |
| 182 | + markdownPaths: string[], |
| 183 | + filteredRedirects: Record<string, string>, |
| 184 | + ) => void, |
| 185 | +): Promise<Record<string, string>> { |
| 186 | + const redirects = await getRedirects(); |
| 187 | + const aliases = await getReversedAliasPaths(); |
| 188 | + const allMarkdownFiles = await getAllMarkdownFiles(CONTENT_DIR); |
| 189 | + const aliasesEntries = Object.entries(aliases); |
| 190 | + |
| 191 | + const markdownPaths = allMarkdownFiles |
| 192 | + .filter( |
| 193 | + slugArray => |
| 194 | + !slugArray[0]?.toLowerCase()?.startsWith('contributor') && |
| 195 | + !slugArray[0]?.toLowerCase()?.startsWith('tags'), |
| 196 | + ) |
| 197 | + .map(slugArray => slugArray.join('/')); |
| 198 | + |
| 199 | + const filteredRedirects: Record<string, string> = {}; |
| 200 | + |
| 201 | + for (const [redirectKey, redirectValue] of Object.entries(redirects)) { |
| 202 | + const normalizedRedirectKey = normalizePathWithSlash(redirectKey); |
| 203 | + const normalizedRedirectValue = normalizePathWithSlash(redirectValue); |
| 204 | + |
| 205 | + const isMatchedAlias = aliasesEntries.some(([aliasKey, aliasVal]) => { |
| 206 | + const normalizedAliasKey = normalizePathWithSlash(aliasKey); |
| 207 | + const normalizedAliasValue = normalizePathWithSlash(aliasVal); |
| 208 | + const isMatchedReversedValues = |
| 209 | + normalizedRedirectValue === normalizedAliasKey || |
| 210 | + normalizedRedirectKey === normalizedAliasValue; |
| 211 | + if (isMatchedReversedValues) { |
| 212 | + return true; |
| 213 | + } |
| 214 | + return normalizedRedirectKey === normalizedAliasKey; |
| 215 | + }); |
| 216 | + |
| 217 | + if (!isMatchedAlias) { |
| 218 | + processRedirect( |
| 219 | + normalizedRedirectKey, |
| 220 | + normalizedRedirectValue, |
| 221 | + aliasesEntries, |
| 222 | + markdownPaths, |
| 223 | + filteredRedirects, |
| 224 | + ); |
| 225 | + } |
| 226 | + } |
| 227 | + return filteredRedirects; |
| 228 | +} |
| 229 | + |
| 230 | +export async function getNginxRedirects(): Promise<Record<string, string>> { |
| 231 | + return filterRedirectsLogic( |
| 232 | + ( |
| 233 | + normalizedRedirectKey, |
| 234 | + normalizedRedirectValue, |
| 235 | + aliasesEntries, |
| 236 | + markdownPaths, |
| 237 | + filteredRedirects, |
| 238 | + ) => { |
| 239 | + const aliasRedirectValue = aliasesEntries.find(([aliasKey]) => { |
| 240 | + const normalizedAliasKey = normalizePathWithSlash(aliasKey); |
| 241 | + return normalizedRedirectValue === normalizedAliasKey; |
| 242 | + }); |
| 243 | + |
| 244 | + if (aliasRedirectValue) { |
| 245 | + filteredRedirects[normalizedRedirectKey] = aliasRedirectValue[1]; |
| 246 | + return; |
| 247 | + } |
| 248 | + |
| 249 | + const isMatchedMdPath = markdownPaths.find( |
| 250 | + mdPath => normalizePathWithSlash(mdPath) === normalizedRedirectValue, |
| 251 | + ); |
| 252 | + if (isMatchedMdPath) { |
| 253 | + filteredRedirects[normalizedRedirectKey] = isMatchedMdPath; |
| 254 | + } |
| 255 | + }, |
| 256 | + ); |
| 257 | +} |
| 258 | + |
| 259 | +export async function getRedirectsNotToAliases(): Promise< |
| 260 | + Record<string, string> |
| 261 | +> { |
| 262 | + return filterRedirectsLogic( |
| 263 | + ( |
| 264 | + normalizedRedirectKey, |
| 265 | + normalizedRedirectValue, |
| 266 | + aliasesEntries, |
| 267 | + markdownPaths, |
| 268 | + filteredRedirects, |
| 269 | + ) => { |
| 270 | + const aliasRedirectValue = aliasesEntries.find(([aliasKey]) => { |
| 271 | + const normalizedAliasKey = normalizePathWithSlash(aliasKey); |
| 272 | + return normalizedRedirectValue === normalizedAliasKey; |
| 273 | + }); |
| 274 | + |
| 275 | + if (!aliasRedirectValue) { |
| 276 | + const isMatchedMdPath = markdownPaths.find( |
| 277 | + mdPath => normalizePathWithSlash(mdPath) === normalizedRedirectValue, |
| 278 | + ); |
| 279 | + if (!isMatchedMdPath) { |
| 280 | + filteredRedirects[normalizedRedirectKey] = normalizedRedirectValue; |
| 281 | + } |
| 282 | + } |
| 283 | + }, |
| 284 | + ); |
| 285 | +} |
0 commit comments