From 4e7a96c5a831882463802cdd4f84fe1464969cb0 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Wed, 23 Mar 2022 12:29:43 +0100 Subject: refactor: use formatjs swc plugin I'm not able to configure SWC plugins in Next.js so to make it works, all translation must have an id. --- src/pages/404.tsx | 5 +++++ src/pages/article/[slug].tsx | 7 +++++++ src/pages/blog/index.tsx | 9 +++++++++ src/pages/blog/page/[id].tsx | 9 ++++++++- src/pages/contact.tsx | 7 +++++++ src/pages/cv.tsx | 7 +++++++ src/pages/index.tsx | 9 +++++++++ src/pages/mentions-legales.tsx | 3 +++ src/pages/projet/[slug].tsx | 2 ++ src/pages/projets.tsx | 2 ++ src/pages/recherche/index.tsx | 9 +++++++++ src/pages/sujet/[slug].tsx | 4 ++++ src/pages/thematique/[slug].tsx | 4 ++++ 13 files changed, 76 insertions(+), 1 deletion(-) (limited to 'src/pages') diff --git a/src/pages/404.tsx b/src/pages/404.tsx index d5b2e86..24c6951 100644 --- a/src/pages/404.tsx +++ b/src/pages/404.tsx @@ -16,12 +16,14 @@ const Error404: NextPageWithLayout = () => { { defaultMessage: 'Error 404: Page not found - {websiteName}', description: '404Page: SEO - Page title', + id: '310o3F', }, { websiteName: settings.name } ); const pageDescription = intl.formatMessage({ defaultMessage: 'Page not found.', description: '404Page: SEO - Meta description', + id: '48Ww//', }); return ( @@ -35,12 +37,14 @@ const Error404: NextPageWithLayout = () => { title={intl.formatMessage({ defaultMessage: 'Page not found', description: '404Page: page title', + id: 'OccTWi', })} />
( @@ -64,6 +68,7 @@ export const getStaticProps: GetStaticProps = async ( const breadcrumbTitle = intl.formatMessage({ defaultMessage: 'Error 404', description: '404Page: breadcrumb item', + id: 'ywkCsK', }); const { locale } = context; const translation = await loadTranslation(locale); diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index 656f7c9..41b84b6 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -140,22 +140,27 @@ const SingleArticle: NextPageWithLayout = ({ const copyText = intl.formatMessage({ defaultMessage: 'Copy', description: 'Prism: copy button text (no clicked)', + id: '/ly3AC', }); const copiedText = intl.formatMessage({ defaultMessage: 'Copied!', description: 'Prism: copy button text (clicked)', + id: 'OV9r1K', }); const errorText = intl.formatMessage({ defaultMessage: 'Use Ctrl+c to copy', description: 'Prism: error text', + id: 'z9qkcQ', }); const darkTheme = intl.formatMessage({ defaultMessage: 'Dark Theme 🌙', description: 'Prism: toggle dark theme button text', + id: 'nFMdWI', }); const lightTheme = intl.formatMessage({ defaultMessage: 'Light Theme 🌞', description: 'Prism: toggle light theme button text', + id: 'Ua2g2p', }); return ( @@ -190,6 +195,7 @@ const SingleArticle: NextPageWithLayout = ({ ariaLabel={intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'ArticlePage: ToC sidebar aria-label', + id: '9nhYRA', })} > @@ -204,6 +210,7 @@ const SingleArticle: NextPageWithLayout = ({ ariaLabel={intl.formatMessage({ defaultMessage: 'Sidebar', description: 'ArticlePage: right sidebar aria-label', + id: 'JeYOeA', })} > diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx index 366fc28..b5ced07 100644 --- a/src/pages/blog/index.tsx +++ b/src/pages/blog/index.tsx @@ -97,6 +97,7 @@ const Blog: NextPageWithLayout = ({ return intl.formatMessage({ defaultMessage: 'Failed to load.', description: 'BlogPage: failed to load text', + id: 'C/XGkH', }); if (!data) return ; @@ -107,6 +108,7 @@ const Blog: NextPageWithLayout = ({ { defaultMessage: 'Blog: development, open source - {websiteName}', description: 'BlogPage: SEO - Page title', + id: '+Y+tLK', }, { websiteName: settings.name } ); @@ -115,6 +117,7 @@ const Blog: NextPageWithLayout = ({ defaultMessage: "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", description: 'BlogPage: SEO - Meta description', + id: '18h/t0', }, { websiteName: settings.name } ); @@ -153,6 +156,7 @@ const Blog: NextPageWithLayout = ({ const title = intl.formatMessage({ defaultMessage: 'Blog', description: 'BlogPage: page title', + id: '7TbbIk', }); return ( @@ -193,6 +197,7 @@ const Blog: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Load more?', description: 'BlogPage: load more text', + id: 'Kqq2cm', })} @@ -205,6 +210,7 @@ const Blog: NextPageWithLayout = ({ title={intl.formatMessage({ defaultMessage: 'Filter by:', description: 'BlogPage: sidebar title', + id: 'KERk7L', })} > = ({ title={intl.formatMessage({ defaultMessage: 'Thematics', description: 'BlogPage: thematics list widget title', + id: 'HriY57', })} /> = ({ title={intl.formatMessage({ defaultMessage: 'Topics', description: 'BlogPage: topics list widget title', + id: '2D9tB5', })} /> @@ -236,6 +244,7 @@ export const getStaticProps: GetStaticProps = async ( const breadcrumbTitle = intl.formatMessage({ defaultMessage: 'Blog', description: 'BlogPage: breadcrumb item', + id: 'R0eDmw', }); const firstPosts = await getPublishedPosts({ first: settings.postsPerPage }); const totalPosts = await getPostsTotal(); diff --git a/src/pages/blog/page/[id].tsx b/src/pages/blog/page/[id].tsx index 2cbd486..6c4d2f8 100644 --- a/src/pages/blog/page/[id].tsx +++ b/src/pages/blog/page/[id].tsx @@ -44,8 +44,9 @@ const BlogPage: NextPageWithLayout = ({ const pageTitle = intl.formatMessage( { - defaultMessage: `Blog - Page {number} - {websiteName}`, + defaultMessage: 'Blog - Page {number} - {websiteName}', description: 'BlogPage: SEO - Page title', + id: '8w+jnD', }, { number: pageNumber, websiteName: settings.name } ); @@ -54,6 +55,7 @@ const BlogPage: NextPageWithLayout = ({ defaultMessage: "Discover {websiteName}'s writings. He talks about web development, Linux and open source mostly.", description: 'BlogPage: SEO - Meta description', + id: '18h/t0', }, { websiteName: settings.name } ); @@ -92,6 +94,7 @@ const BlogPage: NextPageWithLayout = ({ const title = intl.formatMessage({ defaultMessage: 'Blog', description: 'BlogPage: page title', + id: '7TbbIk', }); return ( @@ -123,6 +126,7 @@ const BlogPage: NextPageWithLayout = ({ title={intl.formatMessage({ defaultMessage: 'Filter by:', description: 'BlogPage: sidebar title', + id: 'KERk7L', })} > = ({ title={intl.formatMessage({ defaultMessage: 'Thematics', description: 'BlogPage: thematics list widget title', + id: 'HriY57', })} /> = ({ title={intl.formatMessage({ defaultMessage: 'Topics', description: 'BlogPage: topics list widget title', + id: '2D9tB5', })} /> @@ -154,6 +160,7 @@ export const getStaticProps: GetStaticProps = async ( const breadcrumbTitle = intl.formatMessage({ defaultMessage: 'Blog', description: 'BlogPage: breadcrumb item', + id: 'R0eDmw', }); const { locale, params } = context; const queriedPageNumber = params ? Number(params.id) : 1; diff --git a/src/pages/contact.tsx b/src/pages/contact.tsx index 9f8ec0f..5934dd9 100644 --- a/src/pages/contact.tsx +++ b/src/pages/contact.tsx @@ -22,6 +22,7 @@ const ContactPage: NextPageWithLayout = () => { { defaultMessage: 'Contact form - {websiteName}', description: 'ContactPage: SEO - Page title', + id: 'Y3qRib', }, { websiteName: settings.name } ); @@ -30,6 +31,7 @@ const ContactPage: NextPageWithLayout = () => { defaultMessage: "Contact {websiteName} through its website. All you need to do it's to fill the contact form.", description: 'ContactPage: SEO - Meta description', + id: 'OIffB4', }, { websiteName: settings.name } ); @@ -37,10 +39,12 @@ const ContactPage: NextPageWithLayout = () => { const title = intl.formatMessage({ defaultMessage: 'Contact', description: 'ContactPage: page title', + id: 'AN9iy7', }); const intro = intl.formatMessage({ defaultMessage: 'Please fill the form to contact me.', description: 'ContactPage: page introduction', + id: '8Ls2mD', }); const webpageSchema: WebPage = { @@ -99,6 +103,7 @@ const ContactPage: NextPageWithLayout = () => { {intl.formatMessage({ defaultMessage: 'All fields marked with * are required.', description: 'ContactPage: required fields text', + id: 'txusHd', })}

@@ -108,6 +113,7 @@ const ContactPage: NextPageWithLayout = () => { title={intl.formatMessage({ defaultMessage: 'Find me elsewhere', description: 'ContactPage: social media widget title', + id: 'Qh2CwH', })} github={true} gitlab={true} @@ -128,6 +134,7 @@ export const getStaticProps: GetStaticProps = async ( const breadcrumbTitle = intl.formatMessage({ defaultMessage: 'Contact', description: 'ContactPage: breadcrumb item', + id: 'CzTbM4', }); const { locale } = context; const translation = await loadTranslation(locale); diff --git a/src/pages/cv.tsx b/src/pages/cv.tsx index 39dfeed..71eb449 100644 --- a/src/pages/cv.tsx +++ b/src/pages/cv.tsx @@ -31,6 +31,7 @@ const CV: NextPageWithLayout = () => { { defaultMessage: 'CV Front-end developer - {websiteName}', description: 'CVPage: SEO - Page title', + id: 'Y1ZdJ6', }, { websiteName: settings.name } ); @@ -39,6 +40,7 @@ const CV: NextPageWithLayout = () => { defaultMessage: 'Discover the curriculum of {websiteName}, front-end developer located in France: skills, experiences and training.', description: 'CVPage: SEO - Meta description', + id: 'bBdMGm', }, { websiteName: settings.name } ); @@ -86,6 +88,7 @@ const CV: NextPageWithLayout = () => { { defaultMessage: "{name}'s CV", description: 'CVPage: page title', + id: 'Mj2BQf', }, { name: settings.name } ); @@ -117,6 +120,7 @@ const CV: NextPageWithLayout = () => { ariaLabel={intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'CVPage: ToC sidebar aria-label', + id: 'g4DckL', })} > @@ -129,12 +133,14 @@ const CV: NextPageWithLayout = () => { ariaLabel={intl.formatMessage({ defaultMessage: 'Sidebar', description: 'CVPage: right sidebar aria-label', + id: 'QHOm5t', })} > { title={intl.formatMessage({ defaultMessage: 'Open-source projects', description: 'CVPage: social media widget title', + id: '+Dre5J', })} github={true} gitlab={true} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 513df69..ca0a809 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -38,6 +38,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Web development', description: 'HomePage: link to web development thematic', + id: 'vkF/RP', })} @@ -46,6 +47,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Projects', description: 'HomePage: link to projects', + id: 'N44SOc', })} @@ -84,6 +86,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Free', description: 'HomePage: link to free thematic', + id: 'w8GrOf', })} @@ -92,6 +95,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Linux', description: 'HomePage: link to Linux thematic', + id: 'jASD7k', })} @@ -107,6 +111,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Shaarli', description: 'HomePage: link to Shaarli', + id: 'i5L19t', })} @@ -123,6 +128,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Contact me', description: 'HomePage: contact button text', + id: 'sO/Iwj', })} @@ -132,6 +138,7 @@ const Home: NextPageWithLayout = ({ {intl.formatMessage({ defaultMessage: 'Subscribe', description: 'HomePage: RSS feed subscription text', + id: 'T4YA64', })} @@ -157,6 +164,7 @@ const Home: NextPageWithLayout = ({ { defaultMessage: '{websiteName} | Front-end developer: WordPress/React', description: 'HomePage: SEO - Page title', + id: 'PXp2hv', }, { websiteName: settings.name } ); @@ -165,6 +173,7 @@ const Home: NextPageWithLayout = ({ defaultMessage: '{websiteName} is a front-end developer located in France. He codes and he writes mostly about web development and open-source.', description: 'HomePage: SEO - Meta description', + id: 'tMuNTy', }, { websiteName: settings.name } ); diff --git a/src/pages/mentions-legales.tsx b/src/pages/mentions-legales.tsx index 1bc5c31..b103b5e 100644 --- a/src/pages/mentions-legales.tsx +++ b/src/pages/mentions-legales.tsx @@ -35,6 +35,7 @@ const LegalNotice: NextPageWithLayout = () => { { defaultMessage: 'Legal notice - {websiteName}', description: 'LegalNoticePage: SEO - Page title', + id: '4zAUSu', }, { websiteName: settings.name } ); @@ -42,6 +43,7 @@ const LegalNotice: NextPageWithLayout = () => { { defaultMessage: "Discover the legal notice of {websiteName}'s website.", description: 'LegalNoticePage: SEO - Meta description', + id: 'uvB+32', }, { websiteName: settings.name } ); @@ -49,6 +51,7 @@ const LegalNotice: NextPageWithLayout = () => { const title = intl.formatMessage({ defaultMessage: 'Legal notice', description: 'LegalNoticePage: page title', + id: '/IirIt', }); const publicationDate = new Date(dates.publication); const updateDate = new Date(dates.update); diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx index 51f3d0b..b9a8f39 100644 --- a/src/pages/projet/[slug].tsx +++ b/src/pages/projet/[slug].tsx @@ -117,6 +117,7 @@ const Project: NextPageWithLayout = ({ ariaLabel={intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'ProjectPage: ToC sidebar aria-label', + id: '6dXfvr', })} > @@ -130,6 +131,7 @@ const Project: NextPageWithLayout = ({ ariaLabel={intl.formatMessage({ defaultMessage: 'Sidebar', description: 'ProjectPage: right sidebar aria-label', + id: 'hHrNd0', })} > diff --git a/src/pages/projets.tsx b/src/pages/projets.tsx index 7fb7187..8a81f39 100644 --- a/src/pages/projets.tsx +++ b/src/pages/projets.tsx @@ -28,6 +28,7 @@ const Projects = ({ projects }: { projects: Project[] }) => { { defaultMessage: 'Projects: open-source makings - {websiteName}', description: 'ProjectsPage: SEO - Page title', + id: 'SX1z3t', }, { websiteName: settings.name } ); @@ -36,6 +37,7 @@ const Projects = ({ projects }: { projects: Project[] }) => { defaultMessage: 'Discover {websiteName} projects. Mostly related to web development and open source.', description: 'ProjectsPage: SEO - Meta description', + id: 's6U1Xt', }, { websiteName: settings.name } ); diff --git a/src/pages/recherche/index.tsx b/src/pages/recherche/index.tsx index 42e45cd..b843f8d 100644 --- a/src/pages/recherche/index.tsx +++ b/src/pages/recherche/index.tsx @@ -81,12 +81,14 @@ const Search: NextPageWithLayout = () => { { defaultMessage: 'Search results for {query}', description: 'SearchPage: search results text', + id: 'VSGuGE', }, { query } ) : intl.formatMessage({ defaultMessage: 'Search', description: 'SearchPage: page title', + id: 'U+35YD', }); const description = query @@ -94,6 +96,7 @@ const Search: NextPageWithLayout = () => { { defaultMessage: 'Discover search results for {query}', description: 'SearchPage: meta description with query', + id: 'A4LTGq', }, { query } ) @@ -101,6 +104,7 @@ const Search: NextPageWithLayout = () => { { defaultMessage: 'Search for a post on {websiteName}', description: 'SearchPage: meta description without query', + id: 'PrIz5o', }, { websiteName: settings.name } ); @@ -122,6 +126,7 @@ const Search: NextPageWithLayout = () => { return intl.formatMessage({ defaultMessage: 'Failed to load.', description: 'SearchPage: failed to load text', + id: 'fOe8rH', }); if (!data) return ; @@ -155,6 +160,7 @@ const Search: NextPageWithLayout = () => { {intl.formatMessage({ defaultMessage: 'Load more?', description: 'SearchPage: load more text', + id: 'pEtJik', })} @@ -165,12 +171,14 @@ const Search: NextPageWithLayout = () => { title={intl.formatMessage({ defaultMessage: 'Thematics', description: 'SearchPage: thematics list widget title', + id: 'Dq6+WH', })} /> @@ -188,6 +196,7 @@ export const getStaticProps: GetStaticProps = async ( const breadcrumbTitle = intl.formatMessage({ defaultMessage: 'Search', description: 'SearchPage: breadcrumb item', + id: 'TfU6Qm', }); const { locale } = context; const translation = await loadTranslation(locale); diff --git a/src/pages/sujet/[slug].tsx b/src/pages/sujet/[slug].tsx index bb22a88..30dd36c 100644 --- a/src/pages/sujet/[slug].tsx +++ b/src/pages/sujet/[slug].tsx @@ -138,6 +138,7 @@ const Topic: NextPageWithLayout = ({ topic, allTopics }) => { ariaLabel={intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'TopicPage: ToC sidebar aria-label', + id: 'lsDB5G', })} > @@ -151,6 +152,7 @@ const Topic: NextPageWithLayout = ({ topic, allTopics }) => { { defaultMessage: 'All posts in {name}', description: 'TopicPage: posts list title', + id: 'FLkF2R', }, { name: topic.title } )} @@ -164,6 +166,7 @@ const Topic: NextPageWithLayout = ({ topic, allTopics }) => { ariaLabel={intl.formatMessage({ defaultMessage: 'Sidebar', description: 'TopicPage: right sidebar aria-label', + id: 'eu3beS', })} > @@ -172,6 +175,7 @@ const Topic: NextPageWithLayout = ({ topic, allTopics }) => { title={intl.formatMessage({ defaultMessage: 'Others topics', description: 'TopicPage: topics list widget title', + id: '+4tiVb', })} /> diff --git a/src/pages/thematique/[slug].tsx b/src/pages/thematique/[slug].tsx index edc4296..db22214 100644 --- a/src/pages/thematique/[slug].tsx +++ b/src/pages/thematique/[slug].tsx @@ -128,6 +128,7 @@ const Thematic: NextPageWithLayout = ({ ariaLabel={intl.formatMessage({ defaultMessage: 'Table of Contents', description: 'ThematicPage: ToC sidebar aria-label', + id: 'YwvYfw', })} > @@ -141,6 +142,7 @@ const Thematic: NextPageWithLayout = ({ { defaultMessage: 'All posts in {name}', description: 'ThematicPage: posts list title', + id: 'P7fxX2', }, { name: thematic.title } )} @@ -154,6 +156,7 @@ const Thematic: NextPageWithLayout = ({ ariaLabel={intl.formatMessage({ defaultMessage: 'Sidebar', description: 'ThematicPage: right sidebar aria-label', + id: 'syLgY9', })} > @@ -162,6 +165,7 @@ const Thematic: NextPageWithLayout = ({ title={intl.formatMessage({ defaultMessage: 'Others thematics', description: 'ThematicPage: thematics list widget title', + id: 'norrGp', })} /> -- cgit v1.2.3 From 9226671f49b507ce6f71e6e2c3621014f05f74e9 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Wed, 23 Mar 2022 22:05:30 +0100 Subject: refactor: load prism plugins without babel --- .babelrc | 52 ------- src/components/MDX/CodeBlock/CodeBlock.tsx | 81 +++++++--- .../Settings/PrismThemeToggle/PrismThemeToggle.tsx | 2 +- src/content | 2 +- src/pages/_app.tsx | 2 +- src/pages/article/[slug].tsx | 58 ++++++-- src/pages/projet/[slug].tsx | 1 + src/styles/vendors/_prism.scss | 28 +--- src/ts/types/prism.ts | 51 +++++++ src/utils/helpers/prism.ts | 21 ++- src/utils/providers/prism-theme.tsx | 165 +++++++++++++++++++++ src/utils/providers/prism.tsx | 161 -------------------- 12 files changed, 338 insertions(+), 286 deletions(-) delete mode 100644 .babelrc create mode 100644 src/ts/types/prism.ts create mode 100644 src/utils/providers/prism-theme.tsx delete mode 100644 src/utils/providers/prism.tsx (limited to 'src/pages') diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 657e285..0000000 --- a/.babelrc +++ /dev/null @@ -1,52 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": [ - [ - "prismjs", - { - "languages": [ - "apacheconf", - "bash", - "css", - "diff", - "docker", - "editorconfig", - "ejs", - "git", - "html", - "ignore", - "ini", - "javascript", - "jsdoc", - "json", - "jsx", - "makefile", - "markup", - "php", - "phpdoc", - "regex", - "scss", - "shell-session", - "smarty", - "tcl", - "toml", - "tsx", - "twig", - "yaml" - ], - "plugins": [ - "command-line", - "copy-to-clipboard", - "diff-highlight", - "inline-color", - "line-highlight", - "line-numbers", - "match-braces", - "normalize-whitespace", - "show-language", - "toolbar" - ] - } - ] - ] -} diff --git a/src/components/MDX/CodeBlock/CodeBlock.tsx b/src/components/MDX/CodeBlock/CodeBlock.tsx index 69f0124..c330063 100644 --- a/src/components/MDX/CodeBlock/CodeBlock.tsx +++ b/src/components/MDX/CodeBlock/CodeBlock.tsx @@ -1,28 +1,25 @@ +import { + PrismDefaultPlugins, + PrismLanguages, + PrismPlugins, +} from '@ts/types/prism'; +import { usePrismTheme } from '@utils/providers/prism-theme'; import { useRouter } from 'next/router'; import Prism from 'prismjs'; -import { ReactChildren, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; -import '@utils/plugins/prism-color-scheme'; -import { usePrismTheme } from '@utils/providers/prism'; const CodeBlock = ({ - className, - children, + code, + language, + plugins, }: { - className: string; - children: ReactChildren; + code: string; + language: PrismLanguages; + plugins: PrismPlugins[]; }) => { - const classNames = className.split('+'); - const languageClass = classNames.find((name: string) => - name.startsWith('language-') - ); const intl = useIntl(); const router = useRouter(); - - useEffect(() => { - Prism.highlightAll(); - }, []); - const { setCodeBlocks } = usePrismTheme(); useEffect(() => { @@ -32,6 +29,46 @@ const CodeBlock = ({ setCodeBlocks(allPre); }, [setCodeBlocks, router.asPath]); + const defaultPlugins: PrismDefaultPlugins[] = useMemo( + () => [ + 'autoloader', + 'toolbar', + 'show-language', + 'copy-to-clipboard', + 'color-scheme', + 'match-braces', + 'normalize-whitespace', + ], + [] + ); + + const loadPrismPlugins = useCallback( + async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => { + for (const plugin of prismPlugins) { + try { + if (plugin === 'color-scheme') { + await import(`@utils/plugins/prism-${plugin}`); + } else { + await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`); + + if (plugin === 'autoloader') + Prism.plugins.autoloader.languages_path = '/prism/'; + } + } catch (error) { + console.error('CodeBlock: an error occurred with Prism.'); + console.error(error); + } + } + }, + [] + ); + + useEffect(() => { + loadPrismPlugins([...defaultPlugins, ...plugins]).then(() => { + Prism.highlightAll(); + }); + }, [loadPrismPlugins, defaultPlugins, plugins]); + const copyText = intl.formatMessage({ defaultMessage: 'Copy', description: 'Prism: copy button text (no clicked)', @@ -58,18 +95,20 @@ const CodeBlock = ({ id: 'Ua2g2p', }); + const defaultPluginsClasses = 'match-braces'; + const pluginsClasses = plugins.join(' '); + return ( -
-
-        {children}
-      
-
+ {code} + ); }; diff --git a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx index 9707097..20ad267 100644 --- a/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx +++ b/src/components/Settings/PrismThemeToggle/PrismThemeToggle.tsx @@ -1,7 +1,7 @@ import { Toggle } from '@components/FormElements'; import { MoonIcon, SunIcon } from '@components/Icons'; import Spinner from '@components/Spinner/Spinner'; -import { usePrismTheme } from '@utils/providers/prism'; +import { usePrismTheme } from '@utils/providers/prism-theme'; import { useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; diff --git a/src/content b/src/content index b5aa522..52c97a4 160000 --- a/src/content +++ b/src/content @@ -1 +1 @@ -Subproject commit b5aa522476547db4efa2d6c8e774ca0422ef6547 +Subproject commit 52c97a48f39ef0de9a61d2cf120fae2c70790555 diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index e8c00de..84c2469 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,7 +1,7 @@ import { AppPropsWithLayout } from '@ts/types/app'; import { settings } from '@utils/config'; import { AckeeProvider } from '@utils/providers/ackee'; -import { PrismThemeProvider } from '@utils/providers/prism'; +import { PrismThemeProvider } from '@utils/providers/prism-theme'; import { ThemeProvider } from 'next-themes'; import { useRouter } from 'next/router'; import { IntlProvider } from 'react-intl'; diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index 41b84b6..27a6f7b 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -14,21 +14,20 @@ import { import styles from '@styles/pages/Page.module.scss'; import { NextPageWithLayout } from '@ts/types/app'; import { ArticleMeta, ArticleProps } from '@ts/types/articles'; +import { PrismDefaultPlugins, PrismPlugins } from '@ts/types/prism'; import { settings } from '@utils/config'; import { getFormattedPaths } from '@utils/helpers/format'; import { loadTranslation } from '@utils/helpers/i18n'; import { addPrismClasses } from '@utils/helpers/prism'; -import { usePrismTheme } from '@utils/providers/prism'; import { GetStaticPaths, GetStaticProps, GetStaticPropsContext } from 'next'; import Head from 'next/head'; import { useRouter } from 'next/router'; -import { highlightAll } from 'prismjs'; +import Script from 'next/script'; +import Prism from 'prismjs'; import { ParsedUrlQuery } from 'querystring'; -import { useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import { useIntl } from 'react-intl'; import { Blog, BlogPosting, Graph, WebPage } from 'schema-dts'; -import '@utils/plugins/prism-color-scheme'; -import Script from 'next/script'; const SingleArticle: NextPageWithLayout = ({ comments, @@ -37,19 +36,48 @@ const SingleArticle: NextPageWithLayout = ({ const intl = useIntl(); const router = useRouter(); - useEffect(() => { - addPrismClasses(); - highlightAll(); - }); + const loadPrismPlugins = useCallback( + async (prismPlugins: (PrismDefaultPlugins | PrismPlugins)[]) => { + for (const plugin of prismPlugins) { + try { + if (plugin === 'color-scheme') { + await import(`@utils/plugins/prism-${plugin}`); + } else { + await import(`prismjs/plugins/${plugin}/prism-${plugin}.min.js`); - const { setCodeBlocks } = usePrismTheme(); + if (plugin === 'autoloader') + Prism.plugins.autoloader.languages_path = '/prism/'; + } + } catch (error) { + console.error('Article: an error occurred with Prism.'); + console.error(error); + } + } + }, + [] + ); + + const plugins: (PrismDefaultPlugins | PrismPlugins)[] = useMemo( + () => [ + 'autoloader', + 'toolbar', + 'show-language', + 'copy-to-clipboard', + 'color-scheme', + 'command-line', + 'line-numbers', + 'match-braces', + 'normalize-whitespace', + ], + [] + ); useEffect(() => { - const allPre: NodeListOf = document.querySelectorAll( - 'pre[data-prismjs-color-scheme-current]' - ); - setCodeBlocks(allPre); - }, [setCodeBlocks, router.asPath]); + loadPrismPlugins(plugins).then(() => { + addPrismClasses(); + Prism.highlightAll(); + }); + }, [plugins, loadPrismPlugins]); if (router.isFallback) return ; diff --git a/src/pages/projet/[slug].tsx b/src/pages/projet/[slug].tsx index b9a8f39..1f09fed 100644 --- a/src/pages/projet/[slug].tsx +++ b/src/pages/projet/[slug].tsx @@ -41,6 +41,7 @@ const Project: NextPageWithLayout = ({ }; const components: NestedMDXComponents = { + CodeBlock: (props) => CodeBlock(props), Gallery: (props) => Gallery(props), Image: (props) => ResponsiveImage({ caption: props.caption, ...props }), Link: (props) => Link(props), diff --git a/src/styles/vendors/_prism.scss b/src/styles/vendors/_prism.scss index 2882835..7c05c9f 100644 --- a/src/styles/vendors/_prism.scss +++ b/src/styles/vendors/_prism.scss @@ -43,18 +43,6 @@ } .toolbar-item:nth-child(1) { - grid-column: 2; - grid-row: 1; - margin: 0 var(--spacing-2xs); - - @include mix.media("screen") { - @include mix.dimensions("2xs") { - order: 2; - } - } - } - - .toolbar-item:nth-child(2) { grid-column: 1; grid-row: 1 / 3; margin-right: auto; @@ -64,24 +52,18 @@ color: var(--color-primary-darker); font-size: var(--font-size-sm); font-weight: 600; + } - @include mix.media("screen") { - @include mix.dimensions("2xs") { - order: 1; - } - } + .toolbar-item:nth-child(2) { + grid-column: 2; + grid-row: 1; + margin: 0 var(--spacing-2xs); } .toolbar-item:nth-child(3) { grid-column: 2; grid-row: 2; margin: 0 var(--spacing-2xs); - - @include mix.media("screen") { - @include mix.dimensions("2xs") { - order: 3; - } - } } } diff --git a/src/ts/types/prism.ts b/src/ts/types/prism.ts new file mode 100644 index 0000000..663bc08 --- /dev/null +++ b/src/ts/types/prism.ts @@ -0,0 +1,51 @@ +export type PrismLanguages = + | 'apacheconf' + | 'bash' + | 'css' + | 'diff' + | 'docker' + | 'editorconfig' + | 'ejs' + | 'git' + | 'graphql' + | 'html' + | 'ignore' + | 'ini' + | 'javascript' + | 'jsdoc' + | 'json' + | 'jsx' + | 'makefile' + | 'markup' + | 'php' + | 'phpdoc' + | 'regex' + | 'scss' + | 'shell-session' + | 'smarty' + | 'tcl' + | 'toml' + | 'tsx' + | 'twig' + | 'yaml'; + +export type PrismDefaultPlugins = + | 'autoloader' + | 'color-scheme' + | 'copy-to-clipboard' + | 'match-braces' + | 'normalize-whitespace' + | 'show-language' + | 'toolbar'; + +export type PrismPlugins = + | 'command-line' + | 'diff-highlight' + | 'inline-color' + | 'line-highlight' + | 'line-numbers'; + +export type PrismProviderProps = { + language: PrismLanguages; + plugins: PrismPlugins[]; +}; diff --git a/src/utils/helpers/prism.ts b/src/utils/helpers/prism.ts index bc84c91..a5f5787 100644 --- a/src/utils/helpers/prism.ts +++ b/src/utils/helpers/prism.ts @@ -17,19 +17,18 @@ export const addPrismClasses = () => { const preTags = document.getElementsByTagName('pre'); Array.from(preTags).forEach((preTag) => { - if ( - isLanguageBlock(preTag.classList) && - !preTag.classList.contains('command-line') && - !preTag.classList.contains('language-diff') - ) { - preTag.classList.add('line-numbers', 'match-braces'); - } + if (!isLanguageBlock(preTag.classList)) return; + + preTag.classList.add('match-braces'); - if ( - preTag.classList.contains('command-line') && - preTag.classList.contains('filter-output') - ) { + if (preTag.classList.contains('filter-output')) { preTag.setAttribute('data-filter-output', '#output#'); } + + if (preTag.classList.contains('language-bash')) { + preTag.classList.add('command-line'); + } else if (!preTag.classList.contains('language-diff')) { + preTag.classList.add('line-numbers'); + } }); }; diff --git a/src/utils/providers/prism-theme.tsx b/src/utils/providers/prism-theme.tsx new file mode 100644 index 0000000..2ed8454 --- /dev/null +++ b/src/utils/providers/prism-theme.tsx @@ -0,0 +1,165 @@ +import { LocalStorage } from '@services/local-storage'; +import { + createContext, + FC, + useCallback, + useContext, + useEffect, + useState, +} from 'react'; + +export type PrismTheme = 'dark' | 'light' | 'system'; +export type ResolvedPrismTheme = 'dark' | 'light'; + +export type UsePrismThemeProps = { + themes: PrismTheme[]; + theme?: PrismTheme; + setTheme: (theme: PrismTheme) => void; + resolvedTheme?: ResolvedPrismTheme; + codeBlocks?: NodeListOf; + setCodeBlocks: (codeBlocks: NodeListOf) => void; +}; + +export type PrismThemeProviderProps = { + attribute?: string; + storageKey?: string; + themes?: PrismTheme[]; +}; + +export const PrismThemeContext = createContext({ + themes: ['dark', 'light', 'system'], + setTheme: (_) => { + // This is intentional. + }, + setCodeBlocks: (_) => { + // This is intentional. + }, +}); + +export const usePrismTheme = () => useContext(PrismThemeContext); + +const prefersDarkScheme = () => { + if (typeof window === 'undefined') return; + + return ( + window.matchMedia && + window.matchMedia('(prefers-color-scheme: dark)').matches + ); +}; + +const isValidTheme = (theme: string): boolean => { + return theme === 'dark' || theme === 'light' || theme === 'system'; +}; + +const getTheme = (key: string): PrismTheme | undefined => { + if (typeof window === 'undefined') return undefined; + const storageValue = LocalStorage.get(key); + + return storageValue && isValidTheme(storageValue) + ? (storageValue as PrismTheme) + : undefined; +}; + +export const PrismThemeProvider: FC = ({ + attribute = 'data-prismjs-color-scheme-current', + storageKey = 'prismjs-color-scheme', + themes = ['dark', 'light', 'system'], + children, +}) => { + const getThemeFromSystem = useCallback(() => { + return prefersDarkScheme() ? 'dark' : 'light'; + }, []); + + const [prismTheme, setPrismTheme] = useState( + getTheme(storageKey) || 'system' + ); + + const updateTheme = (theme: PrismTheme) => { + setPrismTheme(theme); + }; + + useEffect(() => { + LocalStorage.set(storageKey, prismTheme); + }, [prismTheme, storageKey]); + + const [resolvedTheme, setResolvedTheme] = useState(); + + useEffect(() => { + if (prismTheme === 'dark' || prismTheme === 'light') { + setResolvedTheme(prismTheme); + } else { + setResolvedTheme(getThemeFromSystem()); + } + }, [prismTheme, getThemeFromSystem]); + + const updateResolvedTheme = useCallback(() => { + setResolvedTheme(getThemeFromSystem()); + }, [getThemeFromSystem]); + + useEffect(() => { + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', updateResolvedTheme); + + return () => + window + .matchMedia('(prefers-color-scheme: dark)') + .removeEventListener('change', updateResolvedTheme); + }, [updateResolvedTheme]); + + const [preTags, setPreTags] = useState>(); + + const updatePreTags = useCallback((tags: NodeListOf) => { + setPreTags(tags); + }, []); + + const updatePreTagsAttribute = useCallback(() => { + preTags?.forEach((pre) => { + pre.setAttribute(attribute, prismTheme); + }); + }, [attribute, preTags, prismTheme]); + + useEffect(() => { + updatePreTagsAttribute(); + }, [updatePreTagsAttribute, prismTheme]); + + const listenAttributeChange = useCallback( + (pre: HTMLPreElement) => { + var observer = new MutationObserver(function (mutations) { + mutations.forEach((record) => { + var mutatedPre = record.target as HTMLPreElement; + var newTheme = mutatedPre.getAttribute(attribute) as PrismTheme; + setPrismTheme(newTheme); + }); + }); + observer.observe(pre, { + attributes: true, + attributeFilter: [attribute], + }); + }, + [attribute] + ); + + useEffect(() => { + if (!preTags) return; + + preTags.forEach((pre) => { + listenAttributeChange(pre); + }); + }, [preTags, listenAttributeChange]); + + return ( + + {children} + + ); +}; diff --git a/src/utils/providers/prism.tsx b/src/utils/providers/prism.tsx deleted file mode 100644 index 7a4221d..0000000 --- a/src/utils/providers/prism.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { LocalStorage } from '@services/local-storage'; -import { - createContext, - FC, - useCallback, - useContext, - useEffect, - useState, -} from 'react'; - -export type PrismTheme = 'dark' | 'light' | 'system'; -export type ResolvedPrismTheme = 'dark' | 'light'; - -export type UsePrismThemeProps = { - themes: PrismTheme[]; - theme?: PrismTheme; - setTheme: (theme: PrismTheme) => void; - resolvedTheme?: ResolvedPrismTheme; - codeBlocks?: NodeListOf; - setCodeBlocks: (codeBlocks: NodeListOf) => void; -}; - -export type PrismThemeProviderProps = { - attribute?: string; - storageKey?: string; - themes?: PrismTheme[]; -}; - -export const PrismThemeContext = createContext({ - themes: ['dark', 'light', 'system'], - setTheme: (_) => {}, - setCodeBlocks: (_) => {}, -}); - -export const usePrismTheme = () => useContext(PrismThemeContext); - -const prefersDarkScheme = () => { - if (typeof window === 'undefined') return; - - return ( - window.matchMedia && - window.matchMedia('(prefers-color-scheme: dark)').matches - ); -}; - -const isValidTheme = (theme: string): boolean => { - return theme === 'dark' || theme === 'light' || theme === 'system'; -}; - -const getTheme = (key: string): PrismTheme | undefined => { - if (typeof window === 'undefined') return undefined; - const storageValue = LocalStorage.get(key); - - return storageValue && isValidTheme(storageValue) - ? (storageValue as PrismTheme) - : undefined; -}; - -export const PrismThemeProvider: FC = ({ - attribute = 'data-prismjs-color-scheme-current', - storageKey = 'prismjs-color-scheme', - themes = ['dark', 'light', 'system'], - children, -}) => { - const getThemeFromSystem = useCallback(() => { - return prefersDarkScheme() ? 'dark' : 'light'; - }, []); - - const [prismTheme, setPrismTheme] = useState( - getTheme(storageKey) || 'system' - ); - - const updateTheme = (theme: PrismTheme) => { - setPrismTheme(theme); - }; - - useEffect(() => { - LocalStorage.set(storageKey, prismTheme); - }, [prismTheme, storageKey]); - - const [resolvedTheme, setResolvedTheme] = useState(); - - useEffect(() => { - if (prismTheme === 'dark' || prismTheme === 'light') { - setResolvedTheme(prismTheme); - } else { - setResolvedTheme(getThemeFromSystem()); - } - }, [prismTheme, getThemeFromSystem]); - - const updateResolvedTheme = useCallback(() => { - setResolvedTheme(getThemeFromSystem()); - }, [getThemeFromSystem]); - - useEffect(() => { - window - .matchMedia('(prefers-color-scheme: dark)') - .addEventListener('change', updateResolvedTheme); - - return () => - window - .matchMedia('(prefers-color-scheme: dark)') - .removeEventListener('change', updateResolvedTheme); - }, [updateResolvedTheme]); - - const [preTags, setPreTags] = useState>(); - - const updatePreTags = useCallback((tags: NodeListOf) => { - setPreTags(tags); - }, []); - - const updatePreTagsAttribute = useCallback(() => { - preTags?.forEach((pre) => { - pre.setAttribute(attribute, prismTheme); - }); - }, [attribute, preTags, prismTheme]); - - useEffect(() => { - updatePreTagsAttribute(); - }, [updatePreTagsAttribute, prismTheme]); - - const listenAttributeChange = useCallback( - (pre: HTMLPreElement) => { - var observer = new MutationObserver(function (mutations) { - mutations.forEach((record) => { - var mutatedPre = record.target as HTMLPreElement; - var newTheme = mutatedPre.getAttribute(attribute) as PrismTheme; - setPrismTheme(newTheme); - }); - }); - observer.observe(pre, { - attributes: true, - attributeFilter: [attribute], - }); - }, - [attribute] - ); - - useEffect(() => { - if (!preTags) return; - - preTags.forEach((pre) => { - listenAttributeChange(pre); - }); - }, [preTags, listenAttributeChange]); - - return ( - - {children} - - ); -}; -- cgit v1.2.3