diff --git a/src/content/docs/features/not-yet-implemented-features.md b/src/content/docs/features/not-yet-implemented-features.md index 66f6302e..ad6c83f4 100644 --- a/src/content/docs/features/not-yet-implemented-features.md +++ b/src/content/docs/features/not-yet-implemented-features.md @@ -1,39 +1,39 @@ -## NOT YET IMPLEMENTED: +# NOT YET IMPLEMENTED: -### Storybook +## Storybook Demo a story for a full page -### I18n +## I18n Add custom error page with i18n name space to remove warning Automated translation extraction: https://react.i18next.com/guides/extracting-translations Remove annoying warnings when getInitialProps is not set -### Material UI and friends +## Material UI and friends Easy switch between MUI, Bootstrap, and probably Tailwind, Styled Components, Emotion... -### Error and logs +## Error and logs Global \_app error boundary Sentry demo Setup debug client side programmatically based on DEBUG environment variable -### Cypress +## Cypress Splitting tests in folders? In order to differentiate real e2e tests from integration testing -### Jest +## Jest Load .env development config automatically in Jest -### GraphQL +## GraphQL Graphql code generator for better autocompletion Demo support of multiple graphQL API using Link split -### Demo custom server for SSR? +## Demo custom server for SSR? NOTE: Using a custom server to serve Next pages is not recommended. We may choose not to support this feature. @@ -41,11 +41,11 @@ ts-node, nodemon to have hot reload Jest for the custom server Fullstack cypress testing/coverage of the custom server -### Next +## Next Remove debug routes from bundle during build -### Others +## Others Pure JS support (no TS), in cypress, in code, in storybook, in jest... Performance testing? diff --git a/src/pages/docs/[...fileName].tsx b/src/pages/docs/[...fileName].tsx deleted file mode 100644 index 9e60682a..00000000 --- a/src/pages/docs/[...fileName].tsx +++ /dev/null @@ -1,80 +0,0 @@ -import renderToString from "next-mdx-remote/render-to-string"; -import hydrate from "next-mdx-remote/hydrate"; -import path from "path"; -import { listMdxFiles, getMdxPaths } from "@vulcanjs/mdx"; -import { promises as fsPromises } from "fs"; -import { Link, Typography } from "@material-ui/core"; - -import matter from "gray-matter"; -import { muiMdComponents } from "~/components/layout/muiMdComponents"; - -// Define components to allow them in your mdx files -// import Test from '../components/test' -// -// You can also replace HTML tags (components is passed to MDXProvider ) -// @see https://mdxjs.com/table-of-components -const components = { - //Test, - ...muiMdComponents, -}; - -const indexLink = ( -
- - Back to documentation index - -
-); - -export default function DocPage({ source, frontMatter /*, filePath*/ }) { - const content = hydrate(source, { - components, - }); - return ( -
- {indexLink} - {content} - {indexLink} - -
- ); -} - -export async function getStaticPaths() { - const docsDir = path.resolve("./src/content/docs"); // relative to the project root - const files = await getMdxPaths(docsDir); - const pageNames = files.map((f) => - f.params.fileName.join('/') - ); - // paths is the file without the extension, shaped as {fileName: [ 'subdirectory', 'file' ]} - const paths = pageNames.map((name) => ({ params: { fileName: name.split('/') } })); - - return { - paths, - fallback: false, // See the "fallback" section below - }; -} - -export async function getStaticProps({ params }) { - const fileName = params.fileName[params.fileName.length - 1]; - let filePath: string; - if (params.fileName.length > 1) { - const directoryName = params.fileName.slice(0, -1); - filePath = path.resolve("./src/content/docs/" + directoryName, fileName + ".md"); // get file - } - else { - filePath = path.resolve("./src/content/docs", fileName + ".md"); // get file - } - // TODO: supports only .md at this point - const source = await fsPromises.readFile(filePath, { encoding: "utf8" }); - // MDX text - can be from a local file, database, anywhere - const { content, data } = matter(source); - // Does a server-render of the source and relevant React wrappers + allow to inject React components - const mdxSource = await renderToString(content, { components }); - return { props: { source: mdxSource, frontMatter: data, filePath } }; -} diff --git a/src/pages/docs/[[...filePath]].tsx b/src/pages/docs/[[...filePath]].tsx new file mode 100644 index 00000000..3580e6b0 --- /dev/null +++ b/src/pages/docs/[[...filePath]].tsx @@ -0,0 +1,173 @@ +import renderToString from "next-mdx-remote/render-to-string"; +import hydrate from "next-mdx-remote/hydrate"; +import path from "path"; +import { getMdxPaths } from "@vulcanjs/mdx"; +import { Link as NextLink } from "@vulcanjs/next-material-ui"; +import { promises as fsPromises, lstatSync, existsSync } from "fs"; +import { List, ListItem, Link, Typography } from "@material-ui/core"; +import matter from "gray-matter"; +import { muiMdComponents } from "~/components/layout/muiMdComponents"; +import { MdxRemote } from "next-mdx-remote/types"; + +// Define components to allow them in your mdx files +// You can also replace HTML tags (components is passed to MDXProvider ) +// @see https://mdxjs.com/table-of-components +const components = { + ...muiMdComponents, +}; + +const indexLink = ( +
+ + Back to documentation index + +
+); + +const homeLink = ( +
+ + Back to home + +
+); + +const header = + + + + VN Live Documentation +; + +interface PageArguments { + pages: Array, + filePath: string, + source: MdxRemote.Source +} +export default function DocPage({ pages, filePath, source }: PageArguments) { + if (source) { // It's a file, not a folder + const content = hydrate(source, { + components, + }); + return ( +
+ {header} + {indexLink} + {content} + {indexLink} + +
+ ); + } + else { // It's a folder + return ( +
+ {header} + {filePath} {/* Print the subfolders we're in */} + + { + pages.map((pageName) => ( + + + + { + pageName.replace( + /-/g, + " " + ) /* we don't use the front matter of the file at this point to simplify loading, so we have to cleanup the name manually */ + } + + + + )) + } +
+ {filePath === '' /* Back home if we're in /docs, back to /docs if we're in a subfolder */ + ? homeLink + : indexLink + } +
+
+ ) + } +} + +interface PathsProps { + params: { filePath: Array } +} +export async function getStaticPaths() { + const docsDir = path.resolve("./src/content/docs"); // relative to the project root + const files = await getMdxPaths(docsDir); + const pageNames = files.map((f) => + f.params.fileName.join('/') + ); + // paths is the file without the extension, shaped as [{ params: { filePath: [ 'subdirectory', 'file' ] } } ] + let paths: Array = [{ params: { filePath: [''] } }]; + for (let name of pageNames) { + const splittedName = name.split('/'); + for (let iNbSplit = 0; iNbSplit < splittedName.length; iNbSplit++) { + paths.push({ params: { filePath: [splittedName[0]] } }); // Add the first subfolder then modify because push directly the array doesn't work + if (iNbSplit > 0) { + for (let i = 1; i < iNbSplit + 1; i++) { + paths[paths.length - 1].params.filePath = paths[paths.length - 1].params.filePath.concat(splittedName[i]) + } + } + } + } + return { + paths, + fallback: false, + }; +} + +/** + * Get the mdx files if the paths leads to a folder, or the content of the mdx file if it is. + * Be sure that a folder returns two elements and a file returns three, or change the implementation of the page consequently. + */ +export async function getStaticProps({ params }) { + // Check if the filePath is a file or a directory + if (!params.filePath) { // We're in /docs + return (await getMdxPages(path.resolve("./src/content/docs"), '')); // relative to the project root + } + else { // we're in a file or a subfolder + let resolvedPath = path.resolve("./src/content/docs/" + params.filePath.join('/')); + if (existsSync(resolvedPath) && lstatSync(resolvedPath).isDirectory()) { // We're in a subfolder + return (await getMdxPages(resolvedPath, params.filePath.join('/'))); + } + else { // We're in a file + resolvedPath = resolvedPath + '.md' + // TODO: handle no .md files + const source = await fsPromises.readFile(resolvedPath, { encoding: "utf8" }); + // MDX text - can be from a local file, database, anywhere + const { content, data } = matter(source); + // Does a server-render of the source and relevant React wrappers + allow to inject React components + const mdxSource = await renderToString(content, { components }); + return { props: { pages: [], filePath: params.filePath.join('/'), source: mdxSource } }; + } + } +} + +/** + * list the .md(x) files in the docs folder + * /!\ Be sure that this function returns only two elements. + */ +const getMdxPages = async (resolvedPath: string, filePath: string) => { + // + // we suppose that the page name is always the file name without extension (no frontmatter URL customization) + // NOTE: if frontMatter is needed, an alternative would be using https://github.com/jescalan/babel-plugin-import-glob-array + // to import all frontMatters + const files = await getMdxPaths(resolvedPath); + const pageNames = files.map((f) => + f.params.fileName[0] + ); + const pages = [...new Set(pageNames.sort())]; // delete duplicates + if (filePath) { + filePath = filePath + '/'; + } + return { props: { pages, filePath: filePath } }; +}; diff --git a/src/pages/docs/index.tsx b/src/pages/docs/index.tsx deleted file mode 100644 index ca5af946..00000000 --- a/src/pages/docs/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import path from "path"; -import { Link } from "@vulcanjs/next-material-ui"; // "next/link"; -import { getMdxPaths } from "@vulcanjs/mdx"; -import { List, ListItem, Typography } from "@material-ui/core"; - -export interface DocIndexProps { - pages: Array; -} -const DocIndex = ({ pages = [] }: DocIndexProps) => ( -
- - - VN Live Documentation - - - {pages.map((pageName) => ( - - - - { - pageName.replace( - /-/g, - " " - ) /* we don't use the front matter of the file at this point to simplify loading, so we have to cleanup the name manually */ - } - - - - ))} -
- - Back to home - -
-
-); - -export const getStaticProps = async () => { - // list the .md(x) files in the docs folder - // we suppose that the page name is always the file name without extension (no frontmatter URL customization) - // NOTE: if frontMatter is needed, an alternative would be using https://github.com/jescalan/babel-plugin-import-glob-array - // to import all frontMatters - const docsDir = path.resolve("./src/content/docs"); // relative to the project root - const files = await getMdxPaths(docsDir); - const pageNames = files.map((f) => - f.params.fileName.join('/') - ); - const pages = pageNames.sort(); - return { props: { pages } }; -}; - -export default DocIndex;