Skip to content
This repository was archived by the owner on May 31, 2023. It is now read-only.

Latest commit

 

History

History
283 lines (213 loc) · 7.22 KB

DESIGN.md

File metadata and controls

283 lines (213 loc) · 7.22 KB

Nextein v4

Major update goal is to reduce foot-print and remove HOCs in favor of getStatic* functions.

  • Remove HOCs withPosts*, withPost.
  • Support [slug] routing.
  • Support [...entry] routing.
  • Support [[...entry]] routing.
  • No bin required. Use next
  • Remove generated .json files in static export.
  • Update README.md
  • Remove page and default page from plugin config.
  • Remove url generation and permalink props. Update tests.
  • Add common sorters (?)

Plugins

The Plugin System will be revamped to split current source into 2 stages: source and create.

We should be able to create remark content based on md files or even plain txt or json files. We could also provide a creator-render perspective that might allow to process and render MDX files or any other formats that does not requires a HAST processing rendering.

Stages

We will have these stages/plugin-types:

  • config
  • source
  • build
  • transform
  • cleanup
  • filter
  • sort
  • render

The stages or plugin-types will be executed in the order listed here.

config

This stage allows plugin developers to inject configuration into the next.config.js module. It MUST return the nextConfig or the modified version of it.

  • config(options, nextConfig): {Object}
    • options
    • nextConfig

source

This stage compiles an entries list. It should call action.build on any entry to be processed. The action.build will execute build plugins.

  • source(options, action: { build, remove }).
    • options
    • action
      • build(buildOptions)
      • remove(removeOptions)
    • buildOptions {Object}
      • filePath: Absolute path
      • path: Relative to options.path. e.g.'/blog/first-post.md'
      • name: e.g.'firts-post.md'
      • mimeType: e.g.'text/markdown'
      • createdOn {Date}
      • extra {Object} default data to be passed for each entry metadata
      • load() {Promise} return file content
    • removeOptions {Object}
      • filePath: Absolute path

build

Create an entry. It should call action.create to generate an Entry in the _entries array.

  • build(options, buildOptions, action: { create }).
    • options
    • buildOptions
    • action
      • create(createOptions): {Entry}
    • createOptions {Object}
      • meta
        • filePath
        • path
        • name
        • mimeType
        • createdOn
        • raw
        • extra: (any user defined data e.g. in frontmatter)
      • content
    • Entry {Object}
      • data
        • __id: MD5(meta.filePath)
        • mimeType: meta.mimeType,
        • name: meta.name
        • category: meta.extra.category || meta.path
        • date: meta.extra.date || meta.created
        • day: {String} 2 digit padded day from date
        • month: {String} 2 digit padded month from date
        • year: {String} 4 digit year from date
        • {...user defined data}
      • content
      • raw

transform

This stage receive the _entries array as posts. Here we can modify one or many items.

  • transform(options, posts): {Array<Entry>}
    • options
    • posts

cleanup

Same as transform stage but useful to remove / clean up data.

  • cleanup(options, posts): {Array<Entry>}
    • options
    • posts

filter

Filter posts. Same as transform stage plugins but guaranteed to run after cleanup.

  • filter(options, posts): {Array<Entry>}
    • options
    • posts

sort

Sort posts. Same as transform stage plugins but guaranteed to run after filter.

  • sort(options, posts): {Array<Entry>}
    • options
    • posts

render

This stage runs inside the Content component.

  • render(options, post): {Component}: It MUST be defined as a named export in a render.js file on the plugin root folder. This method should return a React Component.

Plugin Configuration

The plugin configuration should allow to:

  • Configure multiple instances of a plugin.
  • Override a defined configuration.
  • Resolve fullname plugin with configured name as:
    • simplified: source-fs.
    • fullname: nextein-plugin-source-fs.

Plugin Configuration Resolution

Configuration resolves to an Object with the form:

{ 
  name: {String},
  id?: {String},
  options: {Object}
}

Accepted forms:

  • Object: { name, id?, options }
  • String: name-of-plugin -> { name: 'name-of-plugin' },
  • Array: [name, options] -> { name, options }

A plugin configuration will be identified by an internal id. This id will be set by default to the plugin name if no id property is provided. This allows to generate multiple instances of the same plugin if anid is provided or it can override a pre-configured plugin if not id provided.

{
  name: 'nextein-plugin-x',
  options: {
    position: true,
    raw: false    
  }
},

// override previous definition
['nextein-plugin-x', { position: true, raw: true }], 

// Multiple instances
{
  name: 'source-fs',
  id: 'posts',
  options: {
    path: 'posts',
    includes: '**/*.md'
  }
},
{
  name: 'source-fs',  // resolve to nextein-plugin-source-fs
  id: 'images',
  options: {
    path: 'images'
  }
}

Default Settings

  • source-fs
    • source
  • markdown
    • build
    • cleanup (remove position, raw, etc.)
  • filter-unpublished
    • filter

Watching Sources

TBD.

Rendering with Content

The Content component will run the appropriate render based on the post data.

fetcher

Dynamic Routes and static generator functions (getStaticProps and getStaticPaths) can be used with this new experimental feature.

Example for a [name].js dynamic route

import fetcher from 'nextein/fetcher'

const { getData, getPost } = fetcher(/* filter */)

export async function getStaticPaths () {
  const data = await getData()
  return {
    paths: data.map(({ name }) => ({ params: { name } })),
    fallback: false
  }
}

export async function getStaticProps ({ params }) {
  const post = await getPost(params)
  return { props: { post } }
}

export default function Post ({ post }) {
  //...
}

Example for a [[...name]].js dynamic route:

import fetcher from 'nextein/fetcher'
import { inCategory } from 'nextein/filters'

const { getData, getPosts, getPost } = fetcher(inCategory('guides'))

export async function getStaticPaths () {
  const data = await getData()
  return {
    paths: [{ params: { name: [] } },
      ...data.map(({ name }) => ({ params: { name: [name] } }))
    ],
    fallback: false
  }
}

export async function getStaticProps ({ params }) {
  const posts = await getPosts()
  const post = await getPost(params) // This can be null if not matching `...name`
  return { props: { posts, post } }
}

export default function Guides ({ posts, post }) {
  //...
}