Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0] Refactor and document APIs #1053

Merged
merged 23 commits into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b46a59b
Add basic version of new documentation.js transformer
KyleAMathews May 28, 2017
ab7ba98
Change api 'addFieldToNode' to 'createNodeField'
KyleAMathews May 29, 2017
45f7a31
Fix up node creation and add some simple tests
KyleAMathews May 30, 2017
1067771
Get basic docs page for action creators up
KyleAMathews May 30, 2017
0e39282
change deletePageByPath to just deletePage also sort action creators …
KyleAMathews May 30, 2017
d27833d
Change 'upsertPage' to 'createPage' and document it
KyleAMathews May 30, 2017
8a7575d
Only hide parent object description if its a desctructured object + i…
KyleAMathews May 30, 2017
61b79ff
Update action type
KyleAMathews May 30, 2017
776f77d
Support third level of parameters
KyleAMathews May 30, 2017
8863d25
Document createNode
KyleAMathews May 30, 2017
3697716
change 'addNodeToParent' to 'createParentChildLink' and document
KyleAMathews May 30, 2017
9e1c25a
Change 'addPageDependency' to 'createPageDependency' and document
KyleAMathews May 30, 2017
a8108ab
rename internal APIs
KyleAMathews May 30, 2017
5b0528b
Add links from function names at top to reference
KyleAMathews May 30, 2017
cba9b46
Ignore yarn.lock
KyleAMathews May 30, 2017
3ad7e09
Don't change the changelog...
KyleAMathews May 30, 2017
3c77aa3
Document and slightly modify node APIs
KyleAMathews May 31, 2017
6acd904
Factor out a FunctionsList component for rendering function docs
KyleAMathews May 31, 2017
6d536b3
Add browser/ssr docs and make a few tweaks to APIs
KyleAMathews May 31, 2017
62461fe
Add API specification document
KyleAMathews May 31, 2017
b5d634c
Actually add docs for node/browser/ssr
KyleAMathews May 31, 2017
e6600ab
Tweaks
KyleAMathews May 31, 2017
385bc78
Add README for gatsby-transformer-documentationjs
KyleAMathews May 31, 2017
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"parser": "babel-eslint",
"extends": [
"google",
"eslint:recommended",
Expand Down
6 changes: 3 additions & 3 deletions docs/blog/gatsbygram-case-study/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ const slash = require(`slash`)
// access to any information necessary to programatically
// create pages.
exports.createPages = ({ graphql, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
const { createPage } = boundActionCreators

return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
Expand Down Expand Up @@ -214,9 +214,9 @@ exports.createPages = ({ graphql, boundActionCreators }) => {
// each page's path.
_.each(result.data.allPostsJson.edges, edge => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "upsertPage"
// Plugins and sites can use functions like "createPage"
// to interact with Gatsby.
upsertPage({
createPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
Expand Down
116 changes: 116 additions & 0 deletions docs/docs/api-specification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# API specification

Gatsby's APIs are tailored conceptually to some extent after React.js which
makes sense as users of Gatsby will necessarily also be users of React.js. I
think there's a lot of wins to making Gatsby feel as familiar as possible as
that'll ease adoption.

The two top priorities of the API are a) enable a broad and robust plugin
ecosystem and b) on top of that a broad and robust theme ecosystem (themes
are on the back burner btw until after v1 comes out).

## Plugins

Plugins can extend Gatsby in many ways:

* Sourcing data (e.g from the filesystem or an API or a database)
* Transforming data from one type to another (e.g. a markdown file to HTML)
* Creating pages (e.g. a directory of markdown files all gets turned into
* pages with URLs derived from their file names). Modifying webpack config
* (e.g. for styling options, adding support for other compile-to-js
* languages) Adding things to the rendered HTML (e.g. meta tags, analytics JS
* snippits like Google Analytics) Writing out things to build directory based
* on site data (e.g. service worker, sitemap, RSS feed)

A single plugin can use multiple APIs to accomplish its purpose. E.g. the
plugin for the css-in-js library [Glamor
](/docs/packages/gatsby-plugin-glamor/)

1. modifies the webpack config to add its plugin
2. adds a Babel plugin to replace React's default createElement
3. modifies server rendering to extract
out the critical CSS for each rendered page and inline the CSS in the
`<head>` of that HTML page.

Plugins can also depend on other plugins.
[The Sharp plugin](/docs/packages/gatsby-plugin-sharp/)
exposes a number of high-level APIs for transforming images that several
other Gatsby image plugins depend on.
[gatsby-transformer-remark](/docs/packages/gatsby-transformer-remark/)
does basic markdown->html transformation but exposes an API to allow other
plugins to intervene in the conversion process e.g.
[gatsby-remark-prismjs](/docs/packages/gatsby-remark-prismjs/)
which adds highlighting to code blocks.

Transformer plugins are decoupled from source plugins. Transformer plugins
simply look at the media type of new nodes created by source plugins to
decide if they can transform it or not. Which means that a markdown
transformer plugin can easily transform markdown from any source without any
other configuration e.g. from file, a code comment, or external service like
Trello which supports markdown in some of its data fields.

See [the full list of (official only for now — adding support for community
plugins later) plugins](/docs/plugins/).

# API

## Concepts


* *Page* — a site page with a pathname, a template component, and optional graphql query and layout component
* *Layout Component* — surrounds a page and (eventually) can optionally have a parent layout component as well as a graphql query
* *Template Component* — responsible for rendering N pages. Can optionally have a graphql query
* *Component extensions* — extensions that are resolvable as components. `.js` and `.jsx` are supported by core. But plugins can add support for other compile-to-js languages.
* *Dependency* — Gatsby tracks automatically dependencies between different objects e.g. a page can depend on certain nodes. This allows for hot reloading, caching, incremental rebuilds, etc.
* *Node* — a data object
* *Node Field* — a field added by a plugin to a node that it doesn't control
* *Node Link* — a connection between nodes that gets converted to GraphQL relationships (is there a name for this?). Can be created in a variety of ways as well as automatically inferred. Parent/child links from nodes and their transformed derivative nodes are first class links.

## Operators

* *Create* — make a new thing
* *Get* — get an existing thing
* *Delete* — remove an existing thing
* *Replace* — replace an existing thing
* *Set* — merge into an existing thing

## Extension APIs

Gatsby has multiple processes. The most prominent is the "bootstrap" process.
It has several subprocesses. One tricky part to their design is that they run
both once during the initial bootstrap but also stay alive during development
to continue to respond to changes. This is what drives hot reloading that all
Gatsby data is "alive" and reacts to changes in the environment.

The bootstrap process is as follows:

load site config -> load plugins -> source nodes -> transform nodes -> create
graphql schema -> create pages -> compile component queries -> run queries ->
fin

Once the initial bootstrap is finished, for the development server we start
`webpack-dev-server` and a simple express server for serving files and for a
production build, we start building the css then javascript then HTML with
webpack.

During these processes there are various extension points where plugins can
intervene. All major processes have a `onPre` and `onPost` e.g.
`onPreBootstrap` and `onPostBootstrap` or `onPreBuild` or `onPostBuild`.
During bootstrap, plugins can respond at various stages to APIs like
`onCreatePages`, `onCreateBabelConfig`, and `onSourceNodes`.

At each extension point, Gatsby identifies the plugins which implement the
API and calls them in serial following their order in the site's
`gatsby-config.js`.

In addition to extension APIs in node, plugins can also implement extension
APIs in the server rendering process and the browser e.g. `onClientEntry` or
`onRouteUpdate`

The three main inspirations for this API and spec are React.js' API
specifically @leebyron's email on the React API
(https://gist.github.com/vjeux/f2b015d230cc1ab18ed1df30550495ed), this talk
"How to Design a Good API and Why it Matters" by Joshua Bloch who designed
many parts of Java. https://www.youtube.com/watch?v=heh4OeB9A-c&app=desktop,
and [Hapi.js](https://hapijs.com/api)' plugin design.

20 changes: 10 additions & 10 deletions docs/docs/creating-and-modifying-pages.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ To do this, in your site's `gatsby-node.js` add code
similar to the following:

```javascript
// Implement the Gatsby API “onUpsertPage”. This is
// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onUpsertPage = ({ page, boundActionCreators }) => {
const { upsertPage, deletePageByPath } = boundActionCreators
exports.onCreatePage = ({ page, boundActionCreators }) => {
const { createPage, deletePage } = boundActionCreators

return new Promise((resolve, reject) => {
// Remove trailing slash
Expand All @@ -38,10 +38,10 @@ exports.onUpsertPage = ({ page, boundActionCreators }) => {
if (page.path !== oldPath) {

// Remove the old page
deletePageByPath(oldPath)
deletePage({ path: oldPath })

// Add the new page
upsertPage(page)
createPage(page)
}

resolve()
Expand All @@ -57,10 +57,10 @@ your app that lives under `/app/*`, you want to add code to your `gatsby-node.js
like the following:

```javascript
// Implement the Gatsby API “onUpsertPage”. This is
// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onUpsertPage = async ({ page, boundActionCreators }) => {
const { upsertPage, deletePageByPath } = boundActionCreators
exports.onCreatePage = async ({ page, boundActionCreators }) => {
const { createPage } = boundActionCreators

return new Promise((resolve, reject) => {
// page.matchPath is a special key that's used for matching pages
Expand All @@ -69,7 +69,7 @@ exports.onUpsertPage = async ({ page, boundActionCreators }) => {
page.matchPath = "/app/:path"

// Update the page.
upsertPage(page)
createPage(page)
}

resolve()
Expand All @@ -83,4 +83,4 @@ Often you will need to programmatically create pages. For example, you have
markdown files that each should be a page.

TODO finish this once it's more settled how to modify nodes to add slugs and
other special fields that we want to associate with a node. Perhaps `addFieldToNode`.
other special fields that we want to associate with a node. Perhaps `createNodeField`.
10 changes: 5 additions & 5 deletions docs/docs/migrating-from-v0-to-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ Here's how you do that.
// In your gatsby-node.js
const path = require('path')

exports.onNodeCreate = ({ node, boundActionCreators, getNode }) => {
const { addFieldToNode } = boundActionCreators
exports.onCreateNode = ({ node, boundActionCreators, getNode }) => {
const { createNodeField } = boundActionCreators
let slug
if (node.internal.type === `MarkdownRemark`) {
const fileNode = getNode(node.parent)
Expand All @@ -148,7 +148,7 @@ exports.onNodeCreate = ({ node, boundActionCreators, getNode }) => {
}

// Add slug as a field on the node.
addFieldToNode({ node, fieldName: `slug`, fieldValue: slug })
createNodeField({ node, fieldName: `slug`, fieldValue: slug })
}
}
```
Expand All @@ -158,7 +158,7 @@ Now we can create pages for each markdown file using our slug. In the same

```javascript
exports.createPages = ({ graphql, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
const { createPage } = boundActionCreators

return new Promise((resolve, reject) => {
const pages = []
Expand Down Expand Up @@ -187,7 +187,7 @@ exports.createPages = ({ graphql, boundActionCreators }) => {

// Create blog posts pages.
result.data.allMarkdownRemark.edges.forEach(edge => {
upsertPage({
createPage({
path: edge.node.fields.slug, // required
component: blogPost,
context: {
Expand Down
1 change: 1 addition & 0 deletions docs/docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mean converting this file into a JS component).

## Official plugins

* [gatsby-transformer-documentationjs](/docs/packages/gatsby-transformer-documentationjs/)
* [gatsby-transformer-json](/docs/packages/gatsby-transformer-json/)
* [gatsby-transformer-remark](/docs/packages/gatsby-transformer-remark/)
* [gatsby-transformer-sharp](/docs/packages/gatsby-transformer-sharp/)
Expand Down
8 changes: 4 additions & 4 deletions examples/client-only-paths/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ const Promise = require(`bluebird`)
const path = require(`path`)
const slash = require(`slash`)

// Implement the Gatsby API “onUpsertPage”. This is
// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onUpsertPage = ({ page, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
exports.onCreatePage = ({ page, boundActionCreators }) => {
const { createPage } = boundActionCreators
return new Promise((resolve, reject) => {
// Make the front page match everything client side.
// Normally your paths should be a bit more judicious.
if (page.path === `/`) {
page.matchPath = `/:path`
upsertPage(page)
createPage(page)
}
resolve()
})
Expand Down
6 changes: 3 additions & 3 deletions examples/gatsbygram/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const slash = require(`slash`)
// access to any information necessary to programatically
// create pages.
exports.createPages = ({ graphql, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
const { createPage } = boundActionCreators

return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
Expand Down Expand Up @@ -48,9 +48,9 @@ exports.createPages = ({ graphql, boundActionCreators }) => {
// each page's path.
_.each(result.data.allPostsJson.edges, edge => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "upsertPage"
// Plugins and sites can use functions like "createPage"
// to interact with Gatsby.
upsertPage({
createPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
Expand Down
6 changes: 3 additions & 3 deletions examples/hn/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const slash = require(`slash`)
// access to any information necessary to programatically
// create pages.
exports.createPages = ({ graphql, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
const { createPage } = boundActionCreators
return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
// queries against local Hacker News graphql schema. Think of
Expand Down Expand Up @@ -41,9 +41,9 @@ exports.createPages = ({ graphql, boundActionCreators }) => {
// story page. We'll just use the HN story ID for the slug.
_.each(result.data.allHnStory.edges, edge => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "upsertPage"
// Plugins and sites can use functions like "createPage"
// to interact with Gatsby.
upsertPage({
createPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
Expand Down
8 changes: 4 additions & 4 deletions examples/no-trailing-slashes/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
const Promise = require(`bluebird`)

exports.onUpsertPage = ({ page, boundActionCreators }) => {
const { upsertPage, deletePageByPath } = boundActionCreators
exports.onCreatePage = ({ page, boundActionCreators }) => {
const { createPage, deletePage } = boundActionCreators

return new Promise((resolve, reject) => {
// Remove trailing slash
const oldPath = page.path
page.path = page.path === `/` ? page.path : page.path.replace(/\/$/, ``)
if (page.path !== oldPath) {
// Remove the old page
deletePageByPath(oldPath)
deletePage({ path: oldPath })

// Add the new page
upsertPage(page)
createPage(page)
}

resolve()
Expand Down
6 changes: 3 additions & 3 deletions examples/using-drupal/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const slash = require(`slash`)
// access to any information necessary to programatically
// create pages.
exports.createPages = ({ graphql, boundActionCreators }) => {
const { upsertPage } = boundActionCreators
const { createPage } = boundActionCreators
return new Promise((resolve, reject) => {
// The “graphql” function allows us to run arbitrary
// queries against the local Drupal graphql schema. Think of
Expand Down Expand Up @@ -38,9 +38,9 @@ exports.createPages = ({ graphql, boundActionCreators }) => {
// article node. We'll just use the Drupal NID for the slug.
_.each(result.data.allDrupalNodeArticle.edges, edge => {
// Gatsby uses Redux to manage its internal state.
// Plugins and sites can use functions like "upsertPage"
// Plugins and sites can use functions like "createPage"
// to interact with Gatsby.
upsertPage({
createPage({
// Each page is required to have a `path` as well
// as a template component. The `context` is
// optional but is often necessary so the template
Expand Down
8 changes: 8 additions & 0 deletions packages/gatsby-plugin-catch-links/src/catch-links.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ module.exports = function(root, cb) {
// e.g. _blank.
if (anchor.target && anchor.target.toLowerCase() !== `_self`) return true

// Don't catch links pointed to the same page but with a hash.
if (
anchor.pathname === window.location.pathname &&
anchor.target.hash !== ``
) {
return true
}

// IE clears the host value if the anchor href changed after creation, e.g.
// in React. Creating a new anchor element to ensure host value is present
var a1 = document.createElement(`a`)
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby-plugin-google-analytics/src/gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"

exports.modifyPostBodyComponents = (args, pluginOptions) => {
exports.createPostBodyComponents = (args, pluginOptions) => {
if (process.env.NODE_ENV === `production`) {
return [
<script
Expand Down
2 changes: 1 addition & 1 deletion packages/gatsby-plugin-manifest/src/gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"

exports.modifyHeadComponents = (args, pluginOptions) => [
exports.createHeadComponents = (args, pluginOptions) => [
<link rel="manifest" href="/manifest.json" />,
<meta name="theme-color" content={pluginOptions.theme_color} />,
]
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-offline/src/gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const _ = require(`lodash`)

exports.createPages = ({ boundActionCreators }) => {
if (process.env.NODE_ENV === `production`) {
const { upsertPage } = boundActionCreators
upsertPage({
const { createPage } = boundActionCreators
createPage({
path: `/offline-plugin-app-shell-fallback/`,
component: slash(path.resolve(`${__dirname}/app-shell.js`)),
})
Expand Down
Loading