Skip to content

Commit e843d4e

Browse files
authored
Merge pull request #2 from joshuap/content
Source pages and blocks, display page content
2 parents 8b2bd29 + c62904c commit e843d4e

File tree

6 files changed

+124
-21
lines changed

6 files changed

+124
-21
lines changed

NOTES.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22

33
I'm new to Gatsby. These are my dumb notes.
44

5-
## Getting data from Roam into Gatsby
5+
## Schema
66

7-
I'm sourcing the raw JSON from the filestem into Gatsby's GraphQL schema with the following plugins:
7+
Because of peculiarities of Roam's export format and limitations of Gatsby's JSON transformer plugin (the property name "children" collides w/ Gatsby's internals), I've built my own source plugin for Roam exports based in part on the following plugins:
88

99
- https://www.gatsbyjs.org/packages/gatsby-source-filesystem/
1010
- https://www.gatsbyjs.org/packages/gatsby-transformer-json/
1111

12+
Mine goes a step further and creates nodes for both pages and blocks, which should be useful for cross-referencing.
13+
14+
The display of blocks on pages is currently limited to 10 levels of nesting [due to GraphQL's lack of recursion](https://github.com/graphql/graphql-spec/issues/91#issuecomment-254895093). I think the fragment approach is probably OK (you can use whatever limit makes sense for you), however it may be possible to take a two-pronged approach and embed the full document from the export in each page in addition to adding block nodes.
15+
16+
## Database import
17+
1218
Here's how to export your database in Roam and import it into this Gatsby project:
1319

1420
1. Go to https://roamresearch.com/#/app/yourdb -> Export All
@@ -23,7 +29,7 @@ http://localhost:8000/___graphql
2329

2430
```graphql
2531
{
26-
allRoamJson {
32+
allRoamPage {
2733
edges {
2834
node {
2935
title

gatsby-config.js

-8
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,6 @@ module.exports = {
2727
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
2828
},
2929
},
30-
{
31-
resolve: `gatsby-source-filesystem`,
32-
options: {
33-
name: `roam`,
34-
path: `${__dirname}/db/roam.json`,
35-
},
36-
},
37-
`gatsby-transformer-json`,
3830
// this (optional) plugin enables Progressive Web App + Offline functionality
3931
// To learn more, visit: https://gatsby.dev/offline
4032
// `gatsby-plugin-offline`,

gatsby-node.js

+56-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,61 @@
1+
const _ = require(`lodash`)
12
const path = require(`path`)
3+
const fs = require(`fs-extra`)
24
const crypto = require(`crypto`)
35

6+
exports.sourceNodes = async ({ actions, createNodeId, createContentDigest },
7+
pluginOptions
8+
) => {
9+
const { createNode, createParentChildLink } = actions
10+
11+
if (typeof(pluginOptions.path) === 'undefined') { pluginOptions.path = `db/roam.json` }
12+
if (!path.isAbsolute(pluginOptions.path)) {
13+
pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path)
14+
}
15+
16+
const content = await fs.readFile(pluginOptions.path, `utf-8`)
17+
const parsedContent = JSON.parse(content)
18+
19+
function createChildren(parent, children) {
20+
if (!_.isArray(children)) { return }
21+
children.forEach((obj, i) => {
22+
const nodeMeta = {
23+
id: createNodeId(`roam-block-${parent.id}-${i}`),
24+
parent: parent.id,
25+
children: [],
26+
internal: {
27+
type: `RoamBlock`,
28+
contentDigest: createContentDigest(obj),
29+
},
30+
}
31+
const node = Object.assign({}, obj, nodeMeta)
32+
createNode(node)
33+
createParentChildLink({ parent: parent, child: node })
34+
createChildren(node, obj.children)
35+
})
36+
}
37+
38+
parsedContent.forEach((obj, i) => {
39+
const nodeMeta = {
40+
id: createNodeId(`roam-page-${i}`),
41+
parent: null,
42+
children: [],
43+
internal: {
44+
type: `RoamPage`,
45+
contentDigest: createContentDigest(obj),
46+
},
47+
}
48+
const node = Object.assign({}, obj, nodeMeta)
49+
createNode(node)
50+
createChildren(node, obj.children)
51+
})
52+
}
53+
454
const roamDayRegexp = /(?<month>January|February|March|April|May|June|July|August|September|October|November|December) (?<day>[0-9]{1,2})(?:[a-z]{2})?, (?<year>[0-9]{4})/
555
exports.onCreateNode = ({ node, actions }) => {
656
const { createNodeField } = actions
7-
if (node.internal.type === `RoamJson`) {
57+
58+
if (node.internal.type === `RoamPage`) {
859
let slug
960
const dayMatch = node.title.match(roamDayRegexp)
1061
if (dayMatch) {
@@ -22,7 +73,7 @@ exports.onCreateNode = ({ node, actions }) => {
2273
createNodeField({
2374
node,
2475
name: `slug`,
25-
value: `pages/${slug}`,
76+
value: `/pages/${slug}`,
2677
})
2778
}
2879
}
@@ -33,7 +84,7 @@ exports.createPages = async ({ graphql, actions }) => {
3384
// see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise for more info
3485
const result = await graphql(`
3586
query {
36-
allRoamJson {
87+
allRoamPage {
3788
edges {
3889
node {
3990
fields {
@@ -44,7 +95,7 @@ exports.createPages = async ({ graphql, actions }) => {
4495
}
4596
}
4697
`)
47-
result.data.allRoamJson.edges.forEach(({ node }) => {
98+
result.data.allRoamPage.edges.forEach(({ node }) => {
4899
createPage({
49100
path: node.fields.slug,
50101
component: path.resolve(`./src/templates/page.js`),
@@ -55,4 +106,4 @@ exports.createPages = async ({ graphql, actions }) => {
55106
},
56107
})
57108
})
58-
}
109+
}

src/components/block.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from "react"
2+
3+
export default function Block({ parent }) {
4+
if (!Array.isArray(parent.children)) { return ""; }
5+
return (
6+
<ul>
7+
{parent.children.map((node) => (
8+
<li key={node.id}>
9+
{node.string}{" "}
10+
<Block parent={node}></Block>
11+
</li>
12+
))}
13+
</ul>
14+
)
15+
}

src/pages/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export default function IndexPage({ data }) {
1212
<h1>
1313
All Pages
1414
</h1>
15-
<h4>{data.allRoamJson.totalCount} Pages</h4>
16-
{data.allRoamJson.edges.map(({ node }) => (
15+
<h4>{data.allRoamPage.totalCount} Pages</h4>
16+
{data.allRoamPage.edges.map(({ node }) => (
1717
<div key={node.id}>
1818
<Link to={node.fields.slug}>
1919
<h3>
@@ -29,7 +29,7 @@ export default function IndexPage({ data }) {
2929

3030
export const query = graphql`
3131
query {
32-
allRoamJson {
32+
allRoamPage {
3333
totalCount
3434
edges {
3535
node {

src/templates/page.js

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,59 @@
11
import React from "react"
22
import { graphql } from "gatsby"
33
import Layout from "../components/layout"
4+
import Block from "../components/block"
45

56
export default function Page({ data }) {
6-
const page = data.roamJson
7+
const page = data.roamPage
78
return (
89
<Layout>
910
<h1>{page.title}</h1>
11+
<Block parent={page}></Block>
1012
</Layout>
1113
)
1214
}
1315

16+
// Supports 10-levels of nesting.
17+
// [Why can't it do infinite levels?](https://github.com/graphql/graphql-spec/issues/91#issuecomment-254895093)
1418
export const query = graphql`
1519
query($slug: String!) {
16-
roamJson(fields: { slug: { eq: $slug } }) {
20+
roamPage(fields: { slug: { eq: $slug } }) {
1721
title
22+
children {
23+
...BlockFields
24+
children {
25+
...BlockFields
26+
children {
27+
...BlockFields
28+
children {
29+
...BlockFields
30+
children {
31+
...BlockFields
32+
children {
33+
...BlockFields
34+
children {
35+
...BlockFields
36+
children {
37+
...BlockFields
38+
children {
39+
...BlockFields
40+
children {
41+
...BlockFields
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
}
50+
}
51+
}
1852
}
1953
}
54+
fragment BlockFields on RoamBlock {
55+
id
56+
uid
57+
string
58+
}
2059
`

0 commit comments

Comments
 (0)