@netfoundry/docusaurus-theme 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -0
- package/css/layout.css +14 -0
- package/css/legacy.css +2347 -0
- package/css/theme.css +9 -4
- package/css/vars-dark.css +7 -0
- package/css/vars.css +8 -0
- package/package.json +28 -5
- package/src/components/Alert/Alert.module.css +39 -0
- package/src/components/Alert/Alert.tsx +24 -0
- package/src/components/Alert/index.ts +2 -0
- package/src/components/CodeBlock/CodeBlock.module.css +27 -0
- package/src/components/CodeBlock/CodeBlock.tsx +32 -0
- package/src/components/CodeBlock/index.ts +2 -0
- package/src/components/Common/Common.module.css +0 -0
- package/src/components/Common/Common.tsx +53 -0
- package/src/components/Common/index.ts +1 -0
- package/src/components/NetFoundry/Divider/Divider.module.css +23 -0
- package/src/components/NetFoundry/Divider/Divider.tsx +20 -0
- package/src/components/NetFoundry/Divider/index.ts +1 -0
- package/src/components/NetFoundry/index.ts +1 -0
- package/src/components/NetFoundryFooter/NetFoundryFooter.tsx +164 -0
- package/src/components/NetFoundryFooter/index.ts +1 -0
- package/src/components/NetFoundryFooter/styles.module.css +99 -0
- package/src/components/NetFoundryHighlight/NetFoundryHighlight.tsx +15 -0
- package/src/components/NetFoundryHighlight/index.ts +2 -0
- package/src/components/NetFoundryHighlight/styles.module.css +26 -0
- package/src/components/NetFoundryHorizontalSection/NetFoundryHorizontalSection.tsx +23 -0
- package/src/components/NetFoundryHorizontalSection/index.ts +2 -0
- package/src/components/NetFoundryHorizontalSection/styles.module.css +23 -0
- package/src/components/NetFoundryLayout/NetFoundryLayout.module.css +6 -0
- package/src/components/NetFoundryLayout/NetFoundryLayout.tsx +110 -0
- package/src/components/NetFoundryLayout/index.ts +1 -0
- package/src/components/NetFoundryNavbarItems/NetFoundryNavbarItems.tsx +22 -0
- package/src/components/NetFoundryNavbarItems/index.ts +2 -0
- package/src/components/OsTabs/OsTabs.tsx +30 -0
- package/src/components/OsTabs/index.ts +2 -0
- package/src/components/StarUs/StarUs.tsx +19 -0
- package/src/components/StarUs/index.ts +2 -0
- package/src/components/StarUs/styles.module.css +6 -0
- package/src/components/index.ts +11 -0
- package/src/docusaurus-envhelper.ts +105 -0
- package/src/docusaurus-plugins/index.ts +7 -0
- package/src/docusaurus-plugins/logger.ts +38 -0
- package/src/docusaurus-plugins/remarkCodeSections.ts +115 -0
- package/src/docusaurus-plugins/remarkReplaceMetaUrl.ts +35 -0
- package/src/docusaurus-plugins/remarkScopedPath.ts +70 -0
- package/src/docusaurus-plugins/remarkYamlTable.ts +83 -0
- package/src/docusaurus-plugins/remarkYouTube.ts +56 -0
- package/src/docusaurus-plugins/timedPlugin.ts +22 -0
- package/src/node.ts +6 -0
- package/src/plugins.ts +6 -0
- package/src/ui.ts +7 -0
- package/src/version.ts +1 -0
- package/theme/Layout/index.tsx +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { ReactNode, CSSProperties } from 'react';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import styles from './styles.module.css';
|
|
4
|
+
|
|
5
|
+
export type NetFoundryHorizontalSectionProps = {
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
style?: CSSProperties;
|
|
9
|
+
id?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function NetFoundryHorizontalSection(props: NetFoundryHorizontalSectionProps) {
|
|
13
|
+
const { children, className, style, id } = props;
|
|
14
|
+
return (
|
|
15
|
+
<section
|
|
16
|
+
className={clsx(className, styles.ozHorizontalSectionRoot, styles.ozhsContent)}
|
|
17
|
+
style={style}
|
|
18
|
+
id={id}
|
|
19
|
+
>
|
|
20
|
+
{children}
|
|
21
|
+
</section>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.ozHorizontalSectionRoot {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-grow: 1;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
align-items: center;
|
|
6
|
+
background-color: var(--ifm-background-color);
|
|
7
|
+
max-width: var(--ziti-max-width);
|
|
8
|
+
width: 100%;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ozhsContent {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
max-width: 100%;
|
|
15
|
+
padding: 20px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media (min-width: 1420px) {
|
|
19
|
+
.ozhsContent {
|
|
20
|
+
min-width: var(--ziti-max-width);
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, {JSX} from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import ErrorBoundary from "@docusaurus/ErrorBoundary";
|
|
4
|
+
import { ThemeClassNames } from "@docusaurus/theme-common";
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import { useKeyboardNavigation } from "@docusaurus/theme-common/internal";
|
|
7
|
+
import Head from "@docusaurus/Head";
|
|
8
|
+
import useBaseUrl from "@docusaurus/useBaseUrl";
|
|
9
|
+
|
|
10
|
+
import SkipToContent from "@theme/SkipToContent";
|
|
11
|
+
import AnnouncementBar from "@theme/AnnouncementBar";
|
|
12
|
+
import Navbar from "@theme/Navbar";
|
|
13
|
+
import LayoutProvider from "@theme/Layout/Provider";
|
|
14
|
+
import ErrorPageContent from "@theme/ErrorPageContent";
|
|
15
|
+
import styles from "./NetFoundryLayout.module.css";
|
|
16
|
+
|
|
17
|
+
import { NetFoundryFooter, NetFoundryFooterProps } from "../NetFoundryFooter";
|
|
18
|
+
import { StarUs, StarUsProps } from "../StarUs";
|
|
19
|
+
import { version } from '../../version';
|
|
20
|
+
|
|
21
|
+
export interface MetaProps {
|
|
22
|
+
title?: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
image?: string;
|
|
26
|
+
siteName?: string;
|
|
27
|
+
locale?: string;
|
|
28
|
+
twitterX?: {
|
|
29
|
+
card?: "summary" | "summary_large_image" | "player" | "app";
|
|
30
|
+
site?: string;
|
|
31
|
+
creator?: string;
|
|
32
|
+
imageAlt?: string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface NetFoundryLayoutProps {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
noFooter?: boolean;
|
|
39
|
+
className?: string;
|
|
40
|
+
footerClassName?: string;
|
|
41
|
+
title?: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
starProps?: StarUsProps;
|
|
44
|
+
footerProps?: NetFoundryFooterProps;
|
|
45
|
+
meta?: MetaProps;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function useAbs() {
|
|
49
|
+
return (p?: string) => {
|
|
50
|
+
if (!p) return undefined;
|
|
51
|
+
if (p.startsWith("http")) return p;
|
|
52
|
+
return new URL(useBaseUrl(p), window.location.origin).toString();
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function NetFoundryLayout({
|
|
57
|
+
children,
|
|
58
|
+
noFooter,
|
|
59
|
+
className,
|
|
60
|
+
title,
|
|
61
|
+
description,
|
|
62
|
+
starProps = { repoUrl: "", label: "Star us on GitHub" },
|
|
63
|
+
footerProps,
|
|
64
|
+
meta: metaInput = {},
|
|
65
|
+
}: NetFoundryLayoutProps): JSX.Element {
|
|
66
|
+
useKeyboardNavigation();
|
|
67
|
+
|
|
68
|
+
const abs = useAbs();
|
|
69
|
+
const meta = metaInput;
|
|
70
|
+
const pageTitle =
|
|
71
|
+
(meta.title ?? title ?? "") +
|
|
72
|
+
(meta.siteName ? ` | ${meta.siteName}` : "");
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<LayoutProvider>
|
|
76
|
+
<Head>
|
|
77
|
+
<title>{pageTitle}</title>
|
|
78
|
+
<meta data-rh="true" name="nf-layout-version" content={version} />
|
|
79
|
+
{(meta.description ?? description) && (<meta name="description" content={meta.description ?? description} />)}
|
|
80
|
+
{meta.url && <meta property="og:url" content={meta.url} />}
|
|
81
|
+
{meta.siteName && <meta property="og:site_name" content={meta.siteName} />}
|
|
82
|
+
{meta.title && <meta property="og:title" content={meta.title} />}
|
|
83
|
+
{meta.description && (<meta property="og:description" content={meta.description} />)}
|
|
84
|
+
{meta.image && <meta property="og:image" content={abs(meta.image)} />}
|
|
85
|
+
{meta.locale && <meta property="og:locale" content={meta.locale} />}
|
|
86
|
+
<meta name="twitter:card" content={meta.twitterX?.card ?? "summary_large_image"} />
|
|
87
|
+
{meta.twitterX?.site && (<meta name="twitter:site" content={meta.twitterX.site} />)}
|
|
88
|
+
{meta.twitterX?.creator && (<meta name="twitter:creator" content={meta.twitterX.creator} />)}
|
|
89
|
+
{meta.image && (<meta name="twitter:image" content={abs(meta.image)} />)}
|
|
90
|
+
{meta.twitterX?.imageAlt && (<meta name="twitter:image:alt" content={meta.twitterX.imageAlt} />)}
|
|
91
|
+
</Head>
|
|
92
|
+
<SkipToContent />
|
|
93
|
+
<AnnouncementBar />
|
|
94
|
+
<Navbar />
|
|
95
|
+
{starProps.repoUrl && starProps.label && <StarUs {...starProps} />}
|
|
96
|
+
<div
|
|
97
|
+
className={clsx(
|
|
98
|
+
ThemeClassNames.wrapper.main,
|
|
99
|
+
styles.ozLayoutMainWrapper,
|
|
100
|
+
className,
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
103
|
+
<ErrorBoundary fallback={<ErrorPageContent />}>
|
|
104
|
+
{children}
|
|
105
|
+
</ErrorBoundary>
|
|
106
|
+
{!noFooter && footerProps && <NetFoundryFooter {...footerProps} />}
|
|
107
|
+
</div>
|
|
108
|
+
</LayoutProvider>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './NetFoundryLayout';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type NavbarItem = {
|
|
2
|
+
label: string;
|
|
3
|
+
href?: string;
|
|
4
|
+
to?: string;
|
|
5
|
+
type?: string;
|
|
6
|
+
position?: 'left' | 'right';
|
|
7
|
+
sidebarId?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const NavbarItems: NavbarItem[] = [
|
|
11
|
+
{
|
|
12
|
+
type: 'docSidebar',
|
|
13
|
+
sidebarId: 'tutorialSidebar',
|
|
14
|
+
position: 'left',
|
|
15
|
+
label: 'Docs',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
href: 'https://github.com/facebook/docusaurus',
|
|
19
|
+
label: 'GitHub',
|
|
20
|
+
position: 'right',
|
|
21
|
+
},
|
|
22
|
+
];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React, {useState, useEffect, ReactNode, JSX} from 'react';
|
|
2
|
+
import OriginalTabs from '@theme/Tabs';
|
|
3
|
+
import { osName } from 'react-device-detect';
|
|
4
|
+
import type { ComponentProps } from 'react';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export type OsTabsProps = ComponentProps<typeof OriginalTabs>;
|
|
8
|
+
|
|
9
|
+
export function OsTabs(props: OsTabsProps): JSX.Element {
|
|
10
|
+
const [defaultValue, setDefaultValue] = useState<string | null>(null);
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const tabs = ['iOS', 'Android', 'Mac OS', 'Windows', 'Linux'];
|
|
14
|
+
if (tabs.includes(osName)) {
|
|
15
|
+
setDefaultValue(osName);
|
|
16
|
+
} else {
|
|
17
|
+
setDefaultValue('Windows');
|
|
18
|
+
}
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<OriginalTabs {...props} defaultValue={defaultValue}>
|
|
24
|
+
{props.children}
|
|
25
|
+
</OriginalTabs>
|
|
26
|
+
{/* Uncomment the following line to debug the detected and selected values */}
|
|
27
|
+
{/* <h2>detected={osName}, selected={defaultValue}</h2> */}
|
|
28
|
+
</>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React, {ReactNode} from 'react';
|
|
2
|
+
import styles from './styles.module.css';
|
|
3
|
+
import GitHubButton from 'react-github-btn';
|
|
4
|
+
|
|
5
|
+
export type StarUsProps = {
|
|
6
|
+
repoUrl?: string;
|
|
7
|
+
label?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default function StarUs({repoUrl = '', label = ''}: StarUsProps): ReactNode {
|
|
11
|
+
return (
|
|
12
|
+
<div className={styles.starUsRoot}>
|
|
13
|
+
<span style={{color: "whitesmoke"}}>{label} </span>
|
|
14
|
+
<span style={{height: "20px"}}>
|
|
15
|
+
<GitHubButton href={repoUrl} data-icon="octicon-star" data-show-count="true" aria-label="Star buttons/github-buttons on GitHub">Star</GitHubButton>
|
|
16
|
+
</span>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './Alert'
|
|
2
|
+
export * from './CodeBlock';
|
|
3
|
+
export * from './Common';
|
|
4
|
+
export * from './NetFoundry'
|
|
5
|
+
export * from './NetFoundryFooter';
|
|
6
|
+
export * from './NetFoundryHighlight';
|
|
7
|
+
export * from './NetFoundryHorizontalSection';
|
|
8
|
+
export * from './NetFoundryLayout';
|
|
9
|
+
export * from './NetFoundryNavbarItems';
|
|
10
|
+
export * from './OsTabs';
|
|
11
|
+
export * from './StarUs';
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { LoadContext, Plugin } from '@docusaurus/types';
|
|
2
|
+
|
|
3
|
+
// Safe in browser & Node: check globalThis.process only by property access on a known object.
|
|
4
|
+
// 1) Use Node's env if present; 2) else use browser-injected __DOCUSAURUS_ENV__; 3) else {}
|
|
5
|
+
type Env = Record<string, string | undefined>;
|
|
6
|
+
const g: any = typeof globalThis !== "undefined" ? globalThis : {};
|
|
7
|
+
|
|
8
|
+
const RUNTIME_ENV: Env = (() => {
|
|
9
|
+
if (g.process && typeof g.process.env === "object") return g.process.env as Env;
|
|
10
|
+
if (g.__DOCUSAURUS_ENV__) return g.__DOCUSAURUS_ENV__ as Env;
|
|
11
|
+
return {};
|
|
12
|
+
})();
|
|
13
|
+
|
|
14
|
+
const getEnv = (k: string, d?: string) =>
|
|
15
|
+
(RUNTIME_ENV[k] ?? d) as string | undefined;
|
|
16
|
+
|
|
17
|
+
const ORIGIN =
|
|
18
|
+
getEnv("DOCUSAURUS_URL") ||
|
|
19
|
+
(typeof window !== "undefined" ? window.location.origin : "http://localhost");
|
|
20
|
+
|
|
21
|
+
export const DOCUSAURUS_DEBUG = getEnv("DOCUSAURUS_DEBUG") === "true";
|
|
22
|
+
export const DOCUSAURUS_URL = ORIGIN;
|
|
23
|
+
export const DOCUSAURUS_BASE_PATH = getEnv("DOCUSAURUS_BASE_PATH", "/base-url");
|
|
24
|
+
export const DOCUSAURUS_DOCS_PATH = getEnv("DOCUSAURUS_DOCS_PATH", "/docs-path");
|
|
25
|
+
export const DOCUSAURUS_CANONICAL_DOMAIN = getEnv("DOCUSAURUS_CANONICAL_DOMAIN", "canonical.domain.missing.local");
|
|
26
|
+
export const hotjarId = getEnv("ZITI_HOTJAR_APPID", "6443327")!;
|
|
27
|
+
|
|
28
|
+
export function cleanUrl(path: string) {
|
|
29
|
+
return path.replace(/([^:]\/)\/+/g, "$1");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function docUrl(base: string, path: string): string {
|
|
33
|
+
return cleanUrl(`${base}/${path}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function absoluteUrl(base: string, path: string): string {
|
|
37
|
+
return cleanUrl(`${base}/${DOCUSAURUS_DOCS_PATH}/${path}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function addDocsRedir(base: string, redirectsArr: { to: string; from: string[] }[]) {
|
|
41
|
+
if (getEnv("DEPLOY_ENV") === "kinsta") {
|
|
42
|
+
redirectsArr.push({
|
|
43
|
+
to: docUrl(base, "/learn/introduction/"),
|
|
44
|
+
from: [docUrl(base, "/docs")],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
interface HotjarThemeConfig {
|
|
54
|
+
applicationId: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function pluginHotjar(context: LoadContext): Plugin {
|
|
58
|
+
const { siteConfig } = context;
|
|
59
|
+
const { themeConfig } = siteConfig;
|
|
60
|
+
const { hotjar } = themeConfig as { hotjar?: HotjarThemeConfig };
|
|
61
|
+
|
|
62
|
+
if (!hotjar) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`You need to specify 'hotjar' object in 'themeConfig' with 'applicationId' field in it to use docusaurus-plugin-hotjar`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { applicationId } = hotjar;
|
|
69
|
+
|
|
70
|
+
if (!applicationId) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
'You specified the `hotjar` object in `themeConfig` but the `applicationId` field was missing. ' +
|
|
73
|
+
'Please ensure this is not a mistake.',
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
name: 'docusaurus-plugin-hotjar',
|
|
81
|
+
|
|
82
|
+
injectHtmlTags() {
|
|
83
|
+
console.log(`[hotjar] applicationId = ${applicationId} isProd = ${isProd}`);
|
|
84
|
+
if (!isProd) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
headTags: [
|
|
90
|
+
{
|
|
91
|
+
tagName: 'script',
|
|
92
|
+
innerHTML: `(function(h,o,t,j,a,r){
|
|
93
|
+
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
|
|
94
|
+
h._hjSettings={hjid:${applicationId},hjsv:6};
|
|
95
|
+
a=o.getElementsByTagName('head')[0];
|
|
96
|
+
r=o.createElement('script');r.async=1;
|
|
97
|
+
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
|
|
98
|
+
a.appendChild(r);
|
|
99
|
+
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');`,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { writeFileSync, appendFileSync, existsSync } from 'fs'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
|
|
4
|
+
export enum LogLevel {
|
|
5
|
+
Silent = 0,
|
|
6
|
+
Info = 1,
|
|
7
|
+
Debug = 2,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const LOG = join(process.cwd(), 'remark-plugins.log')
|
|
11
|
+
|
|
12
|
+
export class Logger {
|
|
13
|
+
constructor(private level: LogLevel, private name: string) {}
|
|
14
|
+
log(msg: unknown, level: LogLevel = LogLevel.Info) {
|
|
15
|
+
if (this.level === LogLevel.Silent) return
|
|
16
|
+
if (level > this.level) return
|
|
17
|
+
|
|
18
|
+
const text = String(msg)
|
|
19
|
+
if (!text.trim()) return
|
|
20
|
+
|
|
21
|
+
const line = `[${this.name}] ${LogLevel[level]} ${text}`
|
|
22
|
+
if (!existsSync(LOG)) writeFileSync(LOG, '')
|
|
23
|
+
appendFileSync(LOG, `[${new Date().toISOString()}] ${line}\n`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function resolveLogLevel(val: unknown): LogLevel {
|
|
28
|
+
if (typeof val === "number" && LogLevel[val] !== undefined) {
|
|
29
|
+
return val as LogLevel
|
|
30
|
+
}
|
|
31
|
+
if (typeof val === "string") {
|
|
32
|
+
const key = val[0].toUpperCase() + val.slice(1).toLowerCase()
|
|
33
|
+
if (key in LogLevel) {
|
|
34
|
+
return LogLevel[key as keyof typeof LogLevel]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return LogLevel.Silent
|
|
38
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {visit} from "unist-util-visit"
|
|
2
|
+
import type {Plugin} from "unified"
|
|
3
|
+
import type {Node} from "unist"
|
|
4
|
+
import {Logger, LogLevel, resolveLogLevel} from "./logger"
|
|
5
|
+
|
|
6
|
+
interface Options {
|
|
7
|
+
logLevel?: LogLevel
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const remarkCodeSections: Plugin<[Options?]> = (options?: Options) => {
|
|
11
|
+
const logger = new Logger(resolveLogLevel(options?.logLevel), "remarkCodeSections")
|
|
12
|
+
|
|
13
|
+
const desc_text = "@desc:"
|
|
14
|
+
const command_text = "@command:"
|
|
15
|
+
const code_text = "@code:"
|
|
16
|
+
const results_text = "@results:"
|
|
17
|
+
|
|
18
|
+
return (tree: Node) => {
|
|
19
|
+
visit(tree, "code", (node: any, index: number, parent: any) => {
|
|
20
|
+
if (!node?.value) return
|
|
21
|
+
|
|
22
|
+
const hasActivator = /@desc:|@command:|@code:|@results:/.test(node.value)
|
|
23
|
+
if (!hasActivator) return
|
|
24
|
+
|
|
25
|
+
logger.log(node, LogLevel.Debug)
|
|
26
|
+
if (node.lang && node.lang.startsWith("example")) {
|
|
27
|
+
const lang = node.lang.replace("example-", "").trim()
|
|
28
|
+
const lines = node.value.split("\n")
|
|
29
|
+
let description = "", command = "", code = "", results = "", codeTitle = ""
|
|
30
|
+
let currentSection = ""
|
|
31
|
+
|
|
32
|
+
lines.forEach((line: string) => {
|
|
33
|
+
if (line.startsWith(desc_text)) {
|
|
34
|
+
currentSection = "description"
|
|
35
|
+
description = line.replace(desc_text, "").trim()
|
|
36
|
+
} else if (line.startsWith(command_text)) {
|
|
37
|
+
currentSection = "command"
|
|
38
|
+
command = line.replace(command_text, "").trim()
|
|
39
|
+
} else if (line.startsWith(code_text)) {
|
|
40
|
+
currentSection = "code"
|
|
41
|
+
codeTitle = line.replace(code_text, "").trim()
|
|
42
|
+
} else if (line.startsWith(results_text)) {
|
|
43
|
+
currentSection = "results"
|
|
44
|
+
results = line.replace(results_text, "").trim()
|
|
45
|
+
} else {
|
|
46
|
+
if (currentSection === "description") description += `\n${line}`
|
|
47
|
+
else if (currentSection === "command") command += `\n${line}`
|
|
48
|
+
else if (currentSection === "code") code += `\n${line}`
|
|
49
|
+
else if (currentSection === "results") results += `\n${line}`
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const divWrapper = {
|
|
54
|
+
type: "div",
|
|
55
|
+
data: { hName: "div", hProperties: { className: "code-section" } },
|
|
56
|
+
children: [] as any[],
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (description) {
|
|
60
|
+
const descDiv = {
|
|
61
|
+
type: "div",
|
|
62
|
+
data: { hName: "div", hProperties: { className: "code-section-desc" } },
|
|
63
|
+
children: [] as any[],
|
|
64
|
+
}
|
|
65
|
+
descDiv.children.push(
|
|
66
|
+
{ type: "paragraph", children: [{ type: "strong", children: [{ type: "text", value: "Description:" }] }] },
|
|
67
|
+
{ type: "paragraph", children: [{ type: "text", value: description.trim() }], data: { hProperties: { style: "padding-bottom: 10px;" } } },
|
|
68
|
+
)
|
|
69
|
+
divWrapper.children.push(descDiv)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (command) {
|
|
73
|
+
const cmdDiv = {
|
|
74
|
+
type: "div",
|
|
75
|
+
data: { hName: "div", hProperties: { className: "code-section-command" } },
|
|
76
|
+
children: [] as any[],
|
|
77
|
+
}
|
|
78
|
+
cmdDiv.children.push(
|
|
79
|
+
{ type: "paragraph", children: [{ type: "strong", children: [{ type: "text", value: "Command:" }] }] },
|
|
80
|
+
{ type: "code", lang: "sh", value: command.trim() },
|
|
81
|
+
)
|
|
82
|
+
divWrapper.children.push(cmdDiv)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (code) {
|
|
86
|
+
const codeDiv = {
|
|
87
|
+
type: "div",
|
|
88
|
+
data: { hName: "div", hProperties: { className: "code-section-code" } },
|
|
89
|
+
children: [] as any[],
|
|
90
|
+
}
|
|
91
|
+
codeDiv.children.push(
|
|
92
|
+
{ type: "paragraph", children: [{ type: "strong", children: [{ type: "text", value: codeTitle }] }] },
|
|
93
|
+
{ type: "code", lang, value: code.trim() },
|
|
94
|
+
)
|
|
95
|
+
divWrapper.children.push(codeDiv)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (results) {
|
|
99
|
+
const resultsDiv = {
|
|
100
|
+
type: "div",
|
|
101
|
+
data: { hName: "div", hProperties: { className: "code-section-results" } },
|
|
102
|
+
children: [] as any[],
|
|
103
|
+
}
|
|
104
|
+
resultsDiv.children.push(
|
|
105
|
+
{ type: "paragraph", children: [{ type: "strong", children: [{ type: "text", value: "Results:" }] }] },
|
|
106
|
+
{ type: "code", lang: "buttonless", value: results.trim() },
|
|
107
|
+
)
|
|
108
|
+
divWrapper.children.push(resultsDiv)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
parent.children.splice(index, 1, divWrapper)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Plugin } from 'unified'
|
|
2
|
+
import { visit } from 'unist-util-visit'
|
|
3
|
+
import { Root } from 'mdast'
|
|
4
|
+
import {Logger, LogLevel, resolveLogLevel} from './logger'
|
|
5
|
+
|
|
6
|
+
console.log("🦖 remarkReplaceMetaUrl plugin loaded")
|
|
7
|
+
|
|
8
|
+
interface Options {
|
|
9
|
+
from: string
|
|
10
|
+
to: string
|
|
11
|
+
logLevel?: LogLevel
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const remarkReplaceMetaUrl: Plugin<[Options], Root> = (options?: Options) => {
|
|
15
|
+
const { from = '', to = '', logLevel = LogLevel.Silent } = options ?? {}
|
|
16
|
+
const logger = new Logger(resolveLogLevel(options?.logLevel), 'remarkReplaceMetaUrl')
|
|
17
|
+
|
|
18
|
+
logger.log(`initialized: replacing "${from}" → "${to}"`)
|
|
19
|
+
|
|
20
|
+
return (tree: Root) => {
|
|
21
|
+
visit(tree, 'mdxJsxFlowElement', (node: any) => {
|
|
22
|
+
if (node.name === 'meta' && Array.isArray(node.attributes)) {
|
|
23
|
+
for (const attr of node.attributes) {
|
|
24
|
+
if (attr.name === 'content' && typeof attr.value === 'string' && attr.value.includes(from)) {
|
|
25
|
+
const newVal = attr.value.replace(from, to)
|
|
26
|
+
logger.log(`rewriting: "${attr.value}" → "${newVal}"`, LogLevel.Info)
|
|
27
|
+
attr.value = newVal
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default remarkReplaceMetaUrl
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Plugin } from 'unified'
|
|
2
|
+
import { visit } from 'unist-util-visit'
|
|
3
|
+
import { Image, Link } from 'mdast'
|
|
4
|
+
import { MdxJsxFlowElement, MdxjsEsm } from 'mdast-util-mdx'
|
|
5
|
+
import {Logger, LogLevel, resolveLogLevel} from './logger'
|
|
6
|
+
|
|
7
|
+
console.log("🦖 remarkScopedPath plugin module loaded")
|
|
8
|
+
|
|
9
|
+
interface ScopedPathOptions {
|
|
10
|
+
from: string
|
|
11
|
+
to: string
|
|
12
|
+
}
|
|
13
|
+
interface Options {
|
|
14
|
+
mappings: ScopedPathOptions[]
|
|
15
|
+
logLevel?: LogLevel
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const remarkScopedPath: Plugin<[Options]> = (options?: Options) => {
|
|
19
|
+
const { mappings = [], logLevel = LogLevel.Silent } = options ?? {}
|
|
20
|
+
const logger = new Logger(resolveLogLevel(options?.logLevel), 'remarkScopedPath')
|
|
21
|
+
|
|
22
|
+
logger.log(`initialized with ${mappings.length} mappings`)
|
|
23
|
+
|
|
24
|
+
return (tree, file) => {
|
|
25
|
+
const filePath = file?.path || file?.history?.slice(-1)[0] || 'unknown'
|
|
26
|
+
logger.log(`processing file: ${filePath}`, LogLevel.Debug)
|
|
27
|
+
|
|
28
|
+
const rewrite = (val: string, from: string, to: string, ctx: string) => {
|
|
29
|
+
if (val.startsWith(from)) {
|
|
30
|
+
const newVal = val.replace(from, to)
|
|
31
|
+
logger.log(`🔄 ${ctx} ${val} → ${newVal}`, LogLevel.Info)
|
|
32
|
+
return newVal
|
|
33
|
+
}
|
|
34
|
+
return val
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
visit(tree, 'image', (node: Image) => {
|
|
38
|
+
for (const { from, to } of mappings) node.url = rewrite(node.url, from, to, 'img')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
visit(tree, 'link', (node: Link) => {
|
|
42
|
+
for (const { from, to } of mappings) node.url = rewrite(node.url, from, to, 'link')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
visit(tree, 'mdxJsxFlowElement', (node: MdxJsxFlowElement) => {
|
|
46
|
+
node.attributes?.forEach(attr => {
|
|
47
|
+
if (attr.type === 'mdxJsxAttribute' && typeof attr.value === 'string') {
|
|
48
|
+
for (const { from, to } of mappings)
|
|
49
|
+
attr.value = rewrite(attr.value, from, to, `jsx <${node.name}> ${attr.name}:`)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
visit(tree, 'mdxjsEsm', (node: MdxjsEsm) => {
|
|
55
|
+
for (const { from, to } of mappings) {
|
|
56
|
+
const re = new RegExp(`(['"])${from}/`, 'g')
|
|
57
|
+
const newVal = node.value.replace(re, `$1${to}/`)
|
|
58
|
+
if (newVal !== node.value) {
|
|
59
|
+
logger.log(
|
|
60
|
+
`esm rewrite (${from} → ${to}):\n--- before ---\n${node.value}\n--- after ---\n${newVal}`,
|
|
61
|
+
LogLevel.Info
|
|
62
|
+
)
|
|
63
|
+
node.value = newVal
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
logger.log(` `, LogLevel.Debug)
|
|
69
|
+
}
|
|
70
|
+
}
|