diff --git a/app/components/syntax/Fence.tsx b/app/components/syntax/Fence.tsx
index 87c0d1e..6a138f6 100644
--- a/app/components/syntax/Fence.tsx
+++ b/app/components/syntax/Fence.tsx
@@ -1,46 +1,19 @@
-import { Highlight, Prism } from "prism-react-renderer";
-import { prismThemes } from "@/data/themes/prism";
-import { useTheme } from "@/hooks/useTheme";
-import { Fragment, useMemo } from "react";
-
import { clientOnly } from "vike-react/clientOnly";
+import { SSRSnippet } from "./SSRSnippet";
const CSRSnippet = clientOnly(() => import("./CSRSnippet"));
-function SSRFence({ children, language }: { children: string; language: string }) {
- const { theme } = useTheme();
- const prismTheme = useMemo(() => {
- return prismThemes[theme];
- }, [theme]);
-
- return (
-
- {({ className, style, tokens, getTokenProps }) => (
-
-
- {tokens.map((line, lineIndex) => (
-
- {line
- .filter((token) => !token.empty)
- .map((token, tokenIndex) => (
-
- ))}
- {"\n"}
-
- ))}
-
-
- )}
-
- );
-}
-
export function Fence({ children, language }: { children: string; language: string }) {
+ const props = {
+ language,
+ label: undefined,
+ showLineNumbers: false,
+ children,
+ };
+
return (
- }>
- {children}
-
+ } />
);
}
diff --git a/app/components/syntax/SSRSnippet.tsx b/app/components/syntax/SSRSnippet.tsx
new file mode 100644
index 0000000..c623496
--- /dev/null
+++ b/app/components/syntax/SSRSnippet.tsx
@@ -0,0 +1,58 @@
+import { Highlight, Prism } from "prism-react-renderer";
+import { prismThemes } from "@/data/themes/prism";
+import { useTheme } from "@/hooks/useTheme";
+import { Fragment, useMemo } from "react";
+import clsx from "clsx";
+
+export function SSRSnippet({
+ children,
+ language,
+ label,
+ showLineNumbers = false,
+}: {
+ children: string;
+ language: string;
+ label?: string;
+ showLineNumbers?: boolean;
+}) {
+ const { theme } = useTheme();
+ const prismTheme = useMemo(() => {
+ return prismThemes[theme];
+ }, [theme]);
+
+ return (
+
+ {({ className, style, tokens, getTokenProps }) => (
+
+ {label && (
+
+ {label}
+
+ )}
+
+
+ {tokens.map((line, lineIndex) => (
+
+ {showLineNumbers && (
+
+ {lineIndex + 1}
+
+ )}
+ {line
+ .filter((token) => !token.empty)
+ .map((token, tokenIndex) => (
+
+ ))}
+ {"\n"}
+
+ ))}
+
+
+
+ )}
+
+ );
+}
diff --git a/app/components/syntax/Snippet.tsx b/app/components/syntax/Snippet.tsx
index 4c0ba6a..b7ea54c 100644
--- a/app/components/syntax/Snippet.tsx
+++ b/app/components/syntax/Snippet.tsx
@@ -1,57 +1,11 @@
import type { Data } from "@/pages/docs/+data";
-import { snippetsService } from "@/services/SnippetsService";
-import { Highlight, Prism } from "prism-react-renderer";
import { clientOnly } from "vike-react/clientOnly";
-import { prismThemes } from "@/data/themes/prism";
import { useData } from "vike-react/useData";
-import { useTheme } from "@/hooks/useTheme";
-import { Fragment, useMemo } from "react";
+import { SSRSnippet } from "./SSRSnippet";
const CSRSnippet = clientOnly(() => import("./CSRSnippet"));
-function SSRSnippet({
- language,
- children,
- label,
- showLineNumbers = false,
-}: {
- language: string;
- children: string;
- label?: string;
- showLineNumbers?: boolean;
-}) {
- const { theme } = useTheme();
-
- const prismTheme = useMemo(() => {
- return prismThemes[theme];
- }, [theme]);
-
- return (
- <>
- {label && {label}
}
-
- {({ className, style, tokens, getTokenProps }) => (
-
-
- {tokens.map((line, lineIndex) => (
-
- {line
- .filter((token) => !token.empty)
- .map((token, tokenIndex) => (
-
- ))}
- {"\n"}
-
- ))}
-
-
- )}
-
- >
- );
-}
-
export function Snippet({
path,
language,
diff --git a/app/data/docs/react/usereducer/page.md b/app/data/docs/react/usereducer/page.md
index 0b6e710..9a46bad 100644
--- a/app/data/docs/react/usereducer/page.md
+++ b/app/data/docs/react/usereducer/page.md
@@ -47,19 +47,13 @@ Comme expliqué plus tôt, un reducer est une fonction qui prend en paramètre u
Parlons dans un premier temps de la signature d'un reducer :
{% tabs defaultSelectedTab="jsx" %}
-
{% tab value="jsx" label="JSX" %}
-
-{% snippet path="data/docs/react/usereducer/reducer-example.jsx" language="jsx" showLineNumbers=true /%}
-
+{% snippet path="react/reducer/reducer-example.jsx" language="jsx" showLineNumbers=true /%}
{% /tab %}
{% tab value="tsx" label="TSX" %}
-
-{% snippet path="data/docs/react/usereducer/reducer-example.tsx" language="tsx" showLineNumbers=true /%}
-
+{% snippet path="react/reducer/reducer-example.tsx" language="tsx" showLineNumbers=true /%}
{% /tab %}
-
{% /tabs %}
Comme tu peux le voir, on récupère bien deux paramètres : `state` et `action`.
@@ -82,7 +76,7 @@ En déversant le contenu de l'état actuel, on s'assure de ne pas perdre ces pro
Par exemple :
-{% snippet path="data/docs/react/usereducer/reducer-why-spread-operator.jsx" language="jsx" showLineNumbers=true /%}
+{% snippet path="react/reducer/reducer-why-spread-operator.jsx" language="jsx" showLineNumbers=true /%}
On perdrait ici la propriété `message` si on ne la déversait pas dans le nouvel état.
@@ -103,20 +97,13 @@ Ensuite, on va définir notre état initial :
{% tabs defaultSelectedTab="js" %}
{% tab value="js" label="JavaScript" %}
-```js
-const initialState = { count: 0 };
-```
+{% snippet path="react/reducer/reducer-initial-state.js" language="js" /%}
{% /tab %}
{% tab value="ts" label="TypeScript" %}
-```ts
-type State = {
- count: number;
-};
-const initialState: State = { count: 0 };
-```
+{% snippet path="react/reducer/reducer-initial-state.ts" language="ts" /%}
{% /tab %}
@@ -128,52 +115,13 @@ On peut maintenant définir notre reducer :
{% tab value="js" label="JavaScript" %}
-```js
-const reducer = (state, action) => {
- switch (action.type) {
- case "INCREMENT":
- return { ...state, count: state.count + 1 };
- case "DECREMENT":
- return { ...state, count: state.count - 1 };
- case "RESET":
- return { ...state, count: 0 };
- case "SET":
- return { ...state, count: action.payload };
- default:
- return state;
- }
-};
-```
+{% snippet path="react/reducer/reducer.js" language="js" showLineNumbers=true /%}
{% /tab %}
{% tab value="ts" label="TypeScript" %}
-```ts
-type State = {
- count: number;
-};
-
-type Action = {
- type: "INCREMENT" | "DECREMENT" | "RESET" | "SET";
- payload?: number;
-};
-
-const reducer = (state: State, action: Action) => {
- switch (action.type) {
- case "INCREMENT":
- return { ...state, count: state.count + 1 };
- case "DECREMENT":
- return { ...state, count: state.count - 1 };
- case "RESET":
- return { ...state, count: 0 };
- case "SET":
- return { ...state, count: action.payload! };
- default:
- return state;
- }
-};
-```
+{% snippet path="react/reducer/reducer.ts" language="ts" showLineNumbers=true /%}
{% /tab %}
@@ -196,17 +144,13 @@ Enfin, on peut utiliser le hook useReducer dans notre composant :
{% tab value="js" label="JavaScript" %}
-```js
-const [state, dispatch] = useReducer(reducer, initialState);
-```
+{% snippet path="react/reducer/reducer-hook.js" language="js" /%}
{% /tab %}
{% tab value="ts" label="TypeScript" %}
-```ts
-const [state, dispatch] = useReducer(reducer, initialState);
-```
+{% snippet path="react/reducer/reducer-hook.ts" language="ts" /%}
{% /tab %}
diff --git a/app/data/docs/react/usereducer/reducer-example.jsx b/app/data/snippets/react/reducer/reducer-example.jsx
similarity index 100%
rename from app/data/docs/react/usereducer/reducer-example.jsx
rename to app/data/snippets/react/reducer/reducer-example.jsx
diff --git a/app/data/docs/react/usereducer/reducer-example.tsx b/app/data/snippets/react/reducer/reducer-example.tsx
similarity index 100%
rename from app/data/docs/react/usereducer/reducer-example.tsx
rename to app/data/snippets/react/reducer/reducer-example.tsx
diff --git a/app/data/snippets/react/reducer/reducer-hook.js b/app/data/snippets/react/reducer/reducer-hook.js
new file mode 100644
index 0000000..f6df2d1
--- /dev/null
+++ b/app/data/snippets/react/reducer/reducer-hook.js
@@ -0,0 +1 @@
+const [state, dispatch] = useReducer(reducer, initialState);
diff --git a/app/data/snippets/react/reducer/reducer-hook.ts b/app/data/snippets/react/reducer/reducer-hook.ts
new file mode 100644
index 0000000..d213e28
--- /dev/null
+++ b/app/data/snippets/react/reducer/reducer-hook.ts
@@ -0,0 +1 @@
+const [state, dispatch] = useReducer(reducer, initialState);
diff --git a/app/data/snippets/react/reducer/reducer-initial-state.js b/app/data/snippets/react/reducer/reducer-initial-state.js
new file mode 100644
index 0000000..16982c2
--- /dev/null
+++ b/app/data/snippets/react/reducer/reducer-initial-state.js
@@ -0,0 +1 @@
+const initialState = { count: 0 };
diff --git a/app/data/snippets/react/reducer/reducer-initial-state.ts b/app/data/snippets/react/reducer/reducer-initial-state.ts
new file mode 100644
index 0000000..b9dacf5
--- /dev/null
+++ b/app/data/snippets/react/reducer/reducer-initial-state.ts
@@ -0,0 +1,4 @@
+type State = {
+ count: number;
+};
+const initialState: State = { count: 0 };
diff --git a/app/data/docs/react/usereducer/reducer-why-spread-operator.jsx b/app/data/snippets/react/reducer/reducer-why-spread-operator.jsx
similarity index 100%
rename from app/data/docs/react/usereducer/reducer-why-spread-operator.jsx
rename to app/data/snippets/react/reducer/reducer-why-spread-operator.jsx
diff --git a/app/data/snippets/react/reducer/reducer.js b/app/data/snippets/react/reducer/reducer.js
new file mode 100644
index 0000000..144a007
--- /dev/null
+++ b/app/data/snippets/react/reducer/reducer.js
@@ -0,0 +1,14 @@
+const reducer = (state, action) => {
+ switch (action.type) {
+ case "INCREMENT":
+ return { ...state, count: state.count + 1 };
+ case "DECREMENT":
+ return { ...state, count: state.count - 1 };
+ case "RESET":
+ return { ...state, count: 0 };
+ case "SET":
+ return { ...state, count: action.payload };
+ default:
+ return state;
+ }
+};
diff --git a/app/data/snippets/react/reducer/reducer.ts b/app/data/snippets/react/reducer/reducer.ts
new file mode 100644
index 0000000..465e076
--- /dev/null
+++ b/app/data/snippets/react/reducer/reducer.ts
@@ -0,0 +1,23 @@
+type State = {
+ count: number;
+};
+
+type Action = {
+ type: "INCREMENT" | "DECREMENT" | "RESET" | "SET";
+ payload?: number;
+};
+
+const reducer = (state: State, action: Action) => {
+ switch (action.type) {
+ case "INCREMENT":
+ return { ...state, count: state.count + 1 };
+ case "DECREMENT":
+ return { ...state, count: state.count - 1 };
+ case "RESET":
+ return { ...state, count: 0 };
+ case "SET":
+ return { ...state, count: action.payload! };
+ default:
+ return state;
+ }
+};
diff --git a/app/eslint.config.js b/app/eslint.config.js
index 751c129..1ba6c0c 100644
--- a/app/eslint.config.js
+++ b/app/eslint.config.js
@@ -10,6 +10,7 @@ export default tseslint.config(
{
ignores: [
"dist/*",
+ "data/snippets/*",
// Temporary compiled files
"**/*.ts.build-*.mjs",
diff --git a/app/services/DocsService.ts b/app/services/DocsService.ts
index 602213b..7e1ae09 100644
--- a/app/services/DocsService.ts
+++ b/app/services/DocsService.ts
@@ -20,6 +20,7 @@ type DocData = { title: string; description: string; content: string; sections:
type DocExtension = "mdx" | "md";
class DocsService {
+ private static readonly SNIPPETS_PATH = path.resolve(path.join(__dirname, "data", "snippets"));
private static readonly DOCS_PATH = path.resolve(path.join(__dirname, "data"));
private static readonly DOCS_EXTS: DocExtension[] = ["mdx", "md"]; // Order matters
private static instance: DocsService;
@@ -83,18 +84,23 @@ class DocsService {
}
public fetchSnippets(content: string) {
- const identifierResults = snippetsService.identifyNewSnippets(content);
- if (!identifierResults) return;
+ try {
+ const identifierResults = snippetsService.identifyNewSnippets(content);
+ if (!identifierResults) return [];
- const [snippetsToFetch, allSnippets] = identifierResults;
+ const [snippetsToFetch, allSnippets] = identifierResults;
- for (const snippet of snippetsToFetch) {
- const absolutePath = path.resolve(__dirname, snippet);
- const content = fs.readFileSync(absolutePath, "utf-8");
- snippetsService.setToCache(snippet, content);
+ for (const snippet of snippetsToFetch) {
+ const absolutePath = path.resolve(DocsService.SNIPPETS_PATH, snippet);
+ const content = fs.readFileSync(absolutePath, "utf-8");
+ snippetsService.setToCache(snippet, content);
+ }
+
+ return allSnippets || [];
+ } catch (error) {
+ console.error("Error fetching snippets:", error);
+ return [];
}
-
- return allSnippets;
}
public async fetchDocs() {
@@ -109,15 +115,13 @@ class DocsService {
.replace(`.${extension}`, "")
.replace(/\/$/g, "");
- const allSnippets = this.fetchSnippets(content);
-
const ast = Markdoc.parse(content);
const title = ast.attributes?.frontmatter?.match(/^title:\s*(.*?)\s*$/m)?.[1];
const description = ast.attributes?.frontmatter?.match(/^description:\s*(.*?)\s*$/m)?.[1]?.replaceAll('"', "");
const sections: DocSection[] = [[title, null, []]];
this.extractSections(ast, sections);
- this.setToCache(key, { title, description, content, sections, snippets: allSnippets || [] });
+ this.setToCache(key, { title, description, content, sections, snippets: this.fetchSnippets(content) });
return { key, sections };
}),
@@ -152,6 +156,7 @@ class DocsService {
return doc;
} catch (error) {
+ console.error("Error fetching docs:", error);
return null;
}
}
diff --git a/app/tsconfig.json b/app/tsconfig.json
index 8703a13..a0bf316 100644
--- a/app/tsconfig.json
+++ b/app/tsconfig.json
@@ -20,5 +20,5 @@
"@syntax/*": ["./components/syntax/*"]
}
},
- "exclude": ["dist"]
+ "exclude": ["dist", "data/snippets"]
}