next-netlify-blog-starter icon indicating copy to clipboard operation
next-netlify-blog-starter copied to clipboard

Nested Markdown files? (Information request)

Open brycewray opened this issue 5 years ago • 3 comments

This repo and the accompanying article on the Netlify blog are very helpful!

What additional config, and in which file(s), would be required to let this properly handle routing for Markdown files in nested directories — so that, for example, the final path would be something like example.com/post/dirlevel1/dirlevel2/dirlevel3/my-post for a Markdown file in /posts/dirlevel1/dirlevel2/dirlevel3/? It currently can detect such a file's presence but it's unclear what the correct pathname reference should be in /components/PostList.js to provide the right URL. Most Next.js-to-blog tutorials similarly seem to assume only one level of Markdown posts, but this becomes impractical after a while.

Thanks for any consideration you can give to this request.

brycewray avatar Jun 19 '20 20:06 brycewray

Would greatly appreciate an answer to my question, and I will understand if it's considered a "dumb noob" question. 😄

brycewray avatar Jun 26 '20 00:06 brycewray

Would greatly appreciate an answer to my question, and I will understand if it's considered a "dumb noob" question. 😄

A friend of mine got through with this. I created a nested folder structure using the beta version.

The structure was like this Public -> Content -> Projects -> FolderA, FolderB, FolderA -> Project_1, Project_2, Project_1 -> landing image, cover_image, Project_1.md

The mock-config I used:

  • label: "Projects for folder A" label_singular: 'Project folder A' name: "projects_FolderA" folder: "public/content/projects" create: true media_folder: '' public_folder: '' path: '{{folderName}}/{{title}}/{{slug}}' filter: {field: "folderName", value: "Folder_A"} fields: - {label: "Project Name" , name: "title", widget: string, description: "This is the name of the markdown file."} - {label: "project", name: "folderName", widget: "hidden", default: "Folder_A"} - {label: "anotherProp" , name: "anotherProp", widget: string,"}

I used the hidden widget to hardcode that all projects in this particular setup are listed in Folder_A becuase for my particular project I needed to filter. And this Setup I repeated for Folder_B, etc. I could have made it more dynamic by adding a dropdown with the options Folder_A, Folder_B and setting that value in the path prop above, but if a user would miss-label, the post would end up somewhere else and they might have thought they deleted it.

I then needed a way to: -1 show all projects -2 access their data for path to the image etc. That's where my friend helped and came up with this code:

//folder lib/api.js


  import fs from 'fs'
  import { join } from 'path'
  import matter from 'gray-matter'
  
  const postsDirectory = join(process.cwd(), 'public', 'content', 'projects')
  
  export function getPostSlugs() {
      return walkSync(postsDirectory)
  }
  
  // List all files in a directory in Node.js recursively in a synchronous fashion
  const walkSync = function(dir, filelist) {
      var fs = fs || require('fs'),
          files = fs.readdirSync(dir);
      filelist = filelist || [];
      files.forEach(function(file) {
          if (fs.statSync(dir + '/' + file).isDirectory()) {
              filelist = walkSync(dir + '/' + file, filelist);
          }
          else {
              if(file.endsWith(".md")){
                  console.log(dir, file)
                  let post = {
                      directory: dir,
                      file: file
                  };
  
                  filelist.push(post);
              }
          }
      });
      return filelist;
  };
  
  
  export function getPostBySlug(slug, fields = []) {
    
      const fullPath = slug.directory + '/' + slug.file;
      const fileContents = fs.readFileSync(fullPath, 'utf8')
      const { data, content } = matter(fileContents)
  
      const items = {}
  
      // Ensure only the minimal needed data is exposed
      fields.forEach((field) => {
          if (field === 'slug') {
              items[field] = slug.file
          }
          if (field === 'content') {
              items[field] = content
          }
  
          if (data[field]) {
              items[field] = data[field]
          }
      })
  
      return items
  }
  
  export function getAllPosts(fields = []) {
      const slugs = getPostSlugs()
      const posts = slugs
          .map((slug) => getPostBySlug(slug, fields))
          // sort posts by date in descending order
          .sort((post1, post2) => (post1.date > post2.date ? '-1' : '1'))
      return posts
  }

IN PROJECTS PAGE


  import React, { useEffect } from 'react'
  import { getAllPosts } from '../lib/api'
  
  const Projects = ({ allProjects }) => {
	  return (
		  <div>
			  <ProjectPage projects={allProjects}/>
		  </div>
	  )
  
  }
  
  export async function getStaticProps () {
	  // These are all the props from your markdown file
	  const allProjects = getAllPosts([
		  'title',
		  'date',
		  'slug',
		  'anotherProp',
	  ])
  
	  return {
		  props: { allProjects },
	  }
  }
   
  export default  Projects

Hope this helps happy new year! :)

ReangeloJ avatar Jan 04 '21 21:01 ReangeloJ

Thanks very much, @ReangeloJ! Happy 2021 to you, too.

brycewray avatar Jan 04 '21 22:01 brycewray