forked from aziontech/docs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
remark-fallback-lang.ts
81 lines (68 loc) · 2.82 KB
/
remark-fallback-lang.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import fs from 'fs';
import type { Root } from 'mdast';
import path from 'path';
import type { Plugin, Transformer } from 'unified';
import { visit } from 'unist-util-visit';
export function remarkFallbackLang(): Plugin<[], Root> {
const pageSourceDir = path.resolve('./src/content/docs');
const baseUrl = 'https://docs.astro.build/';
const transformer: Transformer<Root> = (tree, file) => {
const pageUrl = mdFilePathToUrl(file.path, pageSourceDir, baseUrl);
const pageLang = getLanguageCodeFromPathname(pageUrl.pathname);
// Ignore pages without language prefix and English pages
if (!pageLang || pageLang === 'en') return;
visit(tree, 'link', (link) => {
const linkUrl = new URL(link.url, pageUrl);
// Ignore external links
if (pageUrl.host !== linkUrl.host) return;
// Ignore link targets without language prefix
const linkLang = getLanguageCodeFromPathname(linkUrl.pathname);
if (!linkLang) return;
// Ignore link targets that have a valid source file
const linkSourceFileName = tryFindSourceFileForPathname(linkUrl.pathname, pageSourceDir);
if (linkSourceFileName) return;
link.children.push({
type: 'html',
value: ` (EN)`,
});
});
};
return function attacher() {
return transformer;
};
}
export function mdFilePathToUrl(mdFilePath: string, pageSourceDir: string, baseUrl: string) {
const pathBelowRoot = path.relative(pageSourceDir, mdFilePath);
const pathname = pathBelowRoot.replace(/\\/g, '/').replace(/\.mdx?$/i, '/');
return new URL(pathname, baseUrl);
}
export function getLanguageCodeFromPathname(pathname: string) {
// Assuming that `pathname` always starts with a `/`, retrieve the first path part,
// which is usually the language code
const firstPathPart = pathname.split('/')[1];
// Only return parts that look like a two-letter language code
// with optional two-letter country code
if (firstPathPart.match(/^[a-z]{2}(-[a-zA-Z]{2})?$/)) return firstPathPart;
}
/**
* Attempts to find a Markdown source file for the given `pathname`.
*
* Example: Given a pathname of `/en/some-page` or `/en/some-page/`,
* searches for the source file in the following locations
* and returns the first matching path:
* - `${this.pageSourceDir}/en/some-page.md`
* - `${this.pageSourceDir}/en/some-page/index.md`
* - `${this.pageSourceDir}/en/some-page.mdx`
* - `${this.pageSourceDir}/en/some-page/index.mdx`
*
* If no existing file is found, returns `undefined`.
*/
export function tryFindSourceFileForPathname(pathname: string, pageSourceDir: string) {
const possibleSourceFilePaths = [
path.join(pageSourceDir, pathname, '.') + '.md',
path.join(pageSourceDir, pathname, 'index.md'),
path.join(pageSourceDir, pathname, '.') + '.mdx',
path.join(pageSourceDir, pathname, 'index.mdx'),
];
return possibleSourceFilePaths.find((possiblePath) => fs.existsSync(possiblePath));
}