Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
angeloashmore committed Aug 3, 2017
0 parents commit 8c67fc1
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 0 deletions.
19 changes: 19 additions & 0 deletions .babelrc
@@ -0,0 +1,19 @@
{
"sourceMaps": true,
"presets": [
[
"env",
{
"targets": {
"node": 4.0,
"uglify": true,
}
}
]
],
"plugins": [
"transform-runtime",
"transform-object-rest-spread",
"transform-async-to-generator"
]
}
6 changes: 6 additions & 0 deletions .gitignore
@@ -0,0 +1,6 @@
/gatsby-node.js
/normalize.js
/nodes.js
/fetch.js
/yarn.lock
node_modules
34 changes: 34 additions & 0 deletions .npmignore
@@ -0,0 +1,34 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
*.un~
yarn.lock
src
flow-typed
coverage
decls
examples
1 change: 1 addition & 0 deletions index.js
@@ -0,0 +1 @@
// no-op
29 changes: 29 additions & 0 deletions package.json
@@ -0,0 +1,29 @@
{
"name": "gatsby-source-prismic",
"version": "0.1.0",
"description": "Gatsby source plugin for building websites using prismic.io as a data source",
"scripts": {
"build": "babel src --out-dir .",
"watch": "babel -w src --out-dir ."
},
"keywords": [
"gatsby",
"gatsby-plugin",
"gatsby-source-plugin"
],
"author": "Angelo Ashmore <angelo.ashmore@walltowall.com>",
"license": "MIT",
"dependencies": {
"crypto": "^0.0.3",
"json-stringify-safe": "^5.0.1",
"lodash": "^4.17.4",
"prismic-javascript": "^1.1.5"
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-plugin-transform-async-to-generator": "^6.24.1",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.0"
}
}
51 changes: 51 additions & 0 deletions src/fetch.js
@@ -0,0 +1,51 @@
import Prismic from 'prismic-javascript'

module.exports = async ({ repositoryName, accessToken }) => {
console.time(`Fetch Prismic data`)
console.log(`Starting to fetch data from Prismic`)

const apiEndpoint = `https://${repositoryName}.prismic.io/api/v2`
const client = await Prismic.getApi(apiEndpoint)

// Query all documents from client
const documents = await pagedGet(client)

console.timeEnd(`Fetch Prismic data`)

return {
documents
}
}

async function pagedGet(
client,
query = [],
options = {},
page = 1,
pageSize = 100,
aggregatedResponse = null
) {
const mergedOptions = Object.assign({
lang: '*'
}, options)

const response = await client.query(query, {
...mergedOptions,
page,
pageSize
})

if (!aggregatedResponse) {
aggregatedResponse = response.results
} else {
aggregatedResponse = aggregatedResponse.concat(
response.results
)
}

if (page * pageSize < response.total_results_size) {
return pagedGet(client, query, options, page + 1, aggregatedResponse)
}

return aggregatedResponse
}
93 changes: 93 additions & 0 deletions src/gatsby-node.js
@@ -0,0 +1,93 @@
import { uniq } from 'lodash'
import { createHash } from 'crypto'
import stringify from 'json-stringify-safe'
import fetchData from './fetch'
import {
TypeNode,
DocumentNode,
DatumNode
} from './nodes'

const digest = str => createHash(`md5`).update(str).digest(`hex`)

exports.sourceNodes = async (
{ boundActionCreators, getNodes, hasNodeChanged, store },
{ repositoryName, accessToken }
) => {
const {
createNode,
deleteNodes,
touchNode,
setPluginStatus,
} = boundActionCreators

const {
documents
} = await fetchData({
repositoryName,
accessToken
})

// Get list of custom types
const customTypeNames = uniq(documents.map(
doc => doc.type
))

// Custom types do not have IDs, so generate a reproducable one based on the
// name. The MD5 hash of the name is used here.
const customTypeItems = customTypeNames.map(
customTypeName => ({
id: digest(customTypeName),
name: customTypeName
})
)

// Level 1: CustomType nodes
customTypeItems.forEach(customTypeItem => {
const customTypeNode = TypeNode({
customTypeItem
})

const customTypeDocuments = documents.filter(
doc => doc.type === customTypeItem.name
)

// Level 2: CustomTypeDocument nodes
customTypeDocuments.forEach(customTypeDocumentItem => {
const customTypeDocumentNode = DocumentNode({
customTypeItem,
customTypeDocumentItem
})

const customTypeDocumentData = customTypeDocumentItem.data

// Remove slice data
// TODO: Add this functionality
delete customTypeDocumentData.body

// Level 3: CustomTypeDocumentDatum nodes
Object.keys(customTypeDocumentData).forEach(customTypeDocumentDatumKey => {
const customTypeDocumentDatumItem = customTypeDocumentData[customTypeDocumentDatumKey]

// Data do not have IDs, so generate a reproducable one based on the
// content. The MD5 hash of the content is used here.
customTypeDocumentDatumItem.id = digest(stringify(customTypeDocumentDatumItem))

const customTypeDocumentDatumNode = DatumNode({
customTypeDocumentItem,
customTypeDocumentDatumItem
})

createNode(customTypeDocumentDatumNode)
customTypeDocumentNode.children = customTypeDocumentNode.children.concat([customTypeDocumentDatumNode.id])
})

createNode(customTypeDocumentNode)
customTypeNode.children = customTypeNode.children.concat([customTypeDocumentNode.id])
})

createNode(customTypeNode)
})

return
}
95 changes: 95 additions & 0 deletions src/nodes.js
@@ -0,0 +1,95 @@
import { camelCase, upperFirst } from 'lodash'
import { createHash } from 'crypto'
import stringify from 'json-stringify-safe'

const sourceId = `__SOURCE__`
const typePrefix = `Prismic`
const conflictFieldPrefix = `prismic`
const prismicMediaType = `application/x-prismic-v2`
const restrictedNodeFields = [`id`, `children`, `parent`, `fields`, `internal`]

const makeTypeName = type => upperFirst(camelCase(`${typePrefix} ${type}`))
const digest = str => createHash(`md5`).update(str).digest(`hex`)

// TypeNode
//
// Each custom type is a TypeNode. Documents using the custom type are child
// nodes.
export const TypeNode = ({
customTypeItem
}) => {
const node = {
id: customTypeItem.id,
parent: sourceId,
children: [],
name: customTypeItem.name,
internal: {
type: makeTypeName(`${upperFirst(customTypeItem.name)}Type`)
}
}

node.internal.contentDigest = digest(stringify(node))

return node
}

// DocumentNode
//
// Each document is a DocumentNode. Non-content fields (e.g. lang,
// first_puglication_date, slugs) are added as fields on the node. Content
// fields are to be processed into child nodes.
export const DocumentNode = ({
customTypeItem,
customTypeDocumentItem: customTypeDocumentItemOrig
}) => {
const customTypeDocumentItem = Object.assign({}, customTypeDocumentItemOrig)

// Prefix conflicting keys.
Object.keys(customTypeDocumentItem).forEach(key => {
if (restrictedNodeFields.includes(key)) {
customTypeDocumentItem[`${conflictFieldPrefix}${upperFirst(key)}`] = customTypeDocumentItem[key]
delete customTypeDocumentItem[key]
}
})

// Need to use prismicId since the original id key conflicts with Gatsby.
const node = {
id: customTypeDocumentItem.prismicId,
parent: customTypeItem.id,
children: [],
internal: {
type: makeTypeName(`Document`)
},
...customTypeDocumentItem
}

node.internal.contentDigest = digest(stringify(node))

return node
}

// DatumNode
//
// Each document data item is a DatumNode. Prismic's custom JSON content format
// is stringified as the node's content. This will later be parsed again by a
// transformer.
export const DatumNode = ({
customTypeDocumentItem,
customTypeDocumentDatumItem
}) => {
const str = stringify(customTypeDocumentDatumItem)

const node = {
id: customTypeDocumentDatumItem.id,
parent: customTypeDocumentItem.id,
children: [],
internal: {
mediaType: 'application/x-prismic-v2',
type: makeTypeName(`Datum`),
content: str,
contentDigest: digest(str)
}
}

return node
}

0 comments on commit 8c67fc1

Please sign in to comment.