1
0
mirror of https://github.com/kremalicious/blog.git synced 2025-01-03 10:25:07 +01:00

gatsby-node splitup

This commit is contained in:
Matthias Kretschmann 2018-10-14 02:25:24 +02:00
parent ab00dd2a32
commit a502952403
Signed by: m
GPG Key ID: 606EEEF3C479A91F
5 changed files with 212 additions and 196 deletions

View File

@ -42,7 +42,9 @@ The whole [blog](https://kremalicious.com) is a React-based Single Page App buil
### 🎆 EXIF extraction
Automatically extracts EXIF metadata from my photos on build time. For minimal overhead, [fast-exif](https://github.com/titarenko/fast-exif) parses every JPG file upon Gatsby file node creation and adds the extracted EXIF data as node fields. This way EXIF data is only extracted at build time and can be simply queried with GraphQL at run time.
Automatically extracts EXIF metadata from my photos on build time. For minimal overhead, [fast-exif](https://github.com/titarenko/fast-exif) parses every JPG file upon Gatsby file node creation and adds the extracted EXIF data as node fields.
This way, EXIF data is only extracted at build time and can be simply queried with GraphQL at run time.
In the end looks like this, including location display with [pigeon-maps](https://github.com/mariusandra/pigeon-maps):
@ -51,7 +53,7 @@ In the end looks like this, including location display with [pigeon-maps](https:
If you want to know how this works, have a look at the respective component under
- [`src/components/atoms/Exif.jsx`](src/components/atoms/Exif.jsx)
- the EXIF node fields creation in [`gatsby-node.js`](gatsby-node.js)
- the EXIF node fields creation [`gatsby/exif.js`](gatsby/exif.js) running in [`gatsby-node.js`](gatsby-node.js)
### 💰 Cryptocurrency donation via Web3/MetaMask

View File

@ -1,140 +1,26 @@
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')
const fastExif = require('fast-exif')
const Fraction = require('fraction.js')
const dms2dec = require('dms2dec')
const { createMarkdownFields } = require('./gatsby/onCreateNode')
const { createExifFields } = require('./gatsby/exif')
const {
generatePostPages,
generateTagPages,
generateRedirectPages
} = require('./gatsby/createPages')
const { itemsPerPage } = require('./config')
const redirects = [
{ f: '/feed', t: '/feed.xml' },
{ f: '/feed/', t: '/feed.xml' }
]
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
// Markdown files
if (node.internal.type === 'MarkdownRemark') {
createMarkdownNodeFields(node, createNodeField, getNode)
createMarkdownFields(node, createNodeField, getNode)
}
// Image files
if (node.internal.mediaType === 'image/jpeg') {
readAndCreateExifFields(node, createNodeField)
createExifFields(node, createNodeField)
}
}
// Create slug & date for posts from file path values
const createMarkdownNodeFields = (node, createNodeField, getNode) => {
const fileNode = getNode(node.parent)
const parsedFilePath = path.parse(fileNode.relativePath)
const slugOriginal = createFilePath({ node, getNode })
// slug
let slug
if (parsedFilePath.name === 'index') {
slug = `/${parsedFilePath.dir.substring(11)}` // remove date from file dir
} else {
slug = `/${slugOriginal.substring(12)}` // remove first slash & date from file path
}
createNodeField({
node,
name: 'slug',
value: slug
})
// date
let date
if (node.frontmatter.date) {
date = `${node.frontmatter.date}`
} else {
date = `${slugOriginal.substring(1, 10)}` // grab date from file path
}
createNodeField({
node,
name: 'date',
value: date
})
}
const readAndCreateExifFields = (node, createNodeField) => {
fastExif
.read(node.absolutePath, true)
.then(exifData => {
if (!exifData) return
createExifFields(exifData, createNodeField, node)
})
.catch(() => null) // just silently fail when exif can't be extracted
}
const createExifFields = (exifData, createNodeField, node) => {
const { Model } = exifData.image
const {
ISO,
FNumber,
ExposureTime,
FocalLength,
ExposureBiasValue
} = exifData.exif
const {
GPSLatitudeRef,
GPSLatitude,
GPSLongitudeRef,
GPSLongitude
} = exifData.gps
const { n, d } = new Fraction(ExposureTime)
const exposureShortened = parseFloat(ExposureBiasValue.toFixed(2))
const model = `${Model}`
const iso = `ISO ${ISO}`
const fstop = `ƒ ${FNumber}`
const shutterspeed = `${n}/${d}s`
const focalLength = `${FocalLength}mm`
const GPSdec = dms2dec(
GPSLatitude,
GPSLatitudeRef,
GPSLongitude,
GPSLongitudeRef
)
const latitude = GPSdec[0]
const longitude = GPSdec[1]
let exposure
if (ExposureBiasValue === 0) {
exposure = `+/- ${exposureShortened} ev`
} else if (ExposureBiasValue > 0) {
exposure = `+ ${exposureShortened} ev`
} else {
exposure = `${exposureShortened} ev`
}
// add exif fields to type File
createNodeField({
node,
name: 'exif',
value: {
iso,
model,
fstop,
shutterspeed,
focalLength,
exposure,
gps: {
latitude,
longitude
}
}
})
}
exports.createPages = ({ graphql, actions }) => {
const { createPage, createRedirect } = actions
@ -170,85 +56,16 @@ exports.createPages = ({ graphql, actions }) => {
const numPages = Math.ceil(posts.length / itemsPerPage)
// Generate posts & posts index
generateContent(createPage, posts, numPages)
generatePostPages(createPage, posts, numPages)
// Generate Tag Pages
generateTagPages(createPage, posts, numPages)
// create manual redirects
redirects.forEach(({ f, t }) => {
createRedirect({
fromPath: f,
redirectInBrowser: true,
toPath: t
})
})
generateRedirectPages(createRedirect)
resolve()
})
)
})
}
const postsTemplate = path.resolve('src/templates/Posts.jsx')
const generateContent = (createPage, posts, numPages) => {
const postTemplate = path.resolve('src/templates/Post.jsx')
// Create Post pages
posts.forEach(post => {
createPage({
path: `${post.node.fields.slug}`,
component: postTemplate,
context: {
slug: post.node.fields.slug
}
})
})
// Create paginated Blog index pages
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? '/' : `/page/${i + 1}`,
component: postsTemplate,
context: {
limit: itemsPerPage,
skip: i * itemsPerPage,
numPages,
currentPageNumber: i + 1,
prevPage: i - 1,
nextPage: i + 2
}
})
})
}
const generateTagPages = (createPage, posts) => {
const tagList = arrayReducer(posts, 'tags')
tagList.forEach(tag => {
if (tag === 'goodies') return
// Create tag pages
createPage({
path: `/tags/${tag}/`,
component: postsTemplate,
context: { tag }
})
})
}
// https://www.adamjberkowitz.com/tags-and-categories-in-gatsby-js/
const arrayReducer = (postsArray, type) => {
return (postsArray = postsArray
.map(({ node }) => {
return node.frontmatter[type]
})
.reduce((a, b) => {
return a.concat(b)
}, [])
.filter((type, index, array) => {
return array.indexOf(type) === index
})
.sort())
}

81
gatsby/createPages.js Normal file
View File

@ -0,0 +1,81 @@
const path = require('path')
const postsTemplate = path.resolve('src/templates/Posts.jsx')
const redirects = [
{ f: '/feed', t: '/feed.xml' },
{ f: '/feed/', t: '/feed.xml' }
]
exports.generatePostPages = (createPage, posts, numPages) => {
const postTemplate = path.resolve('src/templates/Post.jsx')
// Create Post pages
posts.forEach(post => {
createPage({
path: `${post.node.fields.slug}`,
component: postTemplate,
context: {
slug: post.node.fields.slug
}
})
})
// Create paginated Blog index pages
Array.from({ length: numPages }).forEach((_, i) => {
createPage({
path: i === 0 ? '/' : `/page/${i + 1}`,
component: postsTemplate,
context: {
limit: numPages,
skip: i * numPages,
numPages,
currentPageNumber: i + 1,
prevPage: i - 1,
nextPage: i + 2
}
})
})
}
exports.generateTagPages = (createPage, posts) => {
const tagList = arrayReducer(posts, 'tags')
tagList.forEach(tag => {
if (tag === 'goodies') return
// Create tag pages
createPage({
path: `/tags/${tag}/`,
component: postsTemplate,
context: { tag }
})
})
}
exports.generateRedirectPages = createRedirect => {
redirects.forEach(({ f, t }) => {
createRedirect({
fromPath: f,
redirectInBrowser: true,
toPath: t
})
})
}
//
// ----------------------
//
// https://www.adamjberkowitz.com/tags-and-categories-in-gatsby-js/
const arrayReducer = (postsArray, type) => {
return (postsArray = postsArray
.map(({ node }) => {
return node.frontmatter[type]
})
.reduce((a, b) => {
return a.concat(b)
}, [])
.filter((type, index, array) => {
return array.indexOf(type) === index
})
.sort())
}

77
gatsby/exif.js Normal file
View File

@ -0,0 +1,77 @@
const fastExif = require('fast-exif')
const Fraction = require('fraction.js')
const dms2dec = require('dms2dec')
exports.createExifFields = (node, createNodeField) => {
fastExif
.read(node.absolutePath, true)
.then(exifData => {
if (!exifData) return
constructExifFields(exifData, createNodeField, node)
})
.catch(() => null) // just silently fail when exif can't be extracted
}
const constructExifFields = (exifData, createNodeField, node) => {
const { Model } = exifData.image
const {
ISO,
FNumber,
ExposureTime,
FocalLength,
ExposureBiasValue
} = exifData.exif
const {
GPSLatitudeRef,
GPSLatitude,
GPSLongitudeRef,
GPSLongitude
} = exifData.gps
const { n, d } = new Fraction(ExposureTime)
const exposureShortened = parseFloat(ExposureBiasValue.toFixed(2))
const model = `${Model}`
const iso = `ISO ${ISO}`
const fstop = `ƒ ${FNumber}`
const shutterspeed = `${n}/${d}s`
const focalLength = `${FocalLength}mm`
const GPSdec = dms2dec(
GPSLatitude,
GPSLatitudeRef,
GPSLongitude,
GPSLongitudeRef
)
const latitude = GPSdec[0]
const longitude = GPSdec[1]
let exposure
if (ExposureBiasValue === 0) {
exposure = `+/- ${exposureShortened} ev`
} else if (ExposureBiasValue > 0) {
exposure = `+ ${exposureShortened} ev`
} else {
exposure = `${exposureShortened} ev`
}
// add exif fields to type File
createNodeField({
node,
name: 'exif',
value: {
iso,
model,
fstop,
shutterspeed,
focalLength,
exposure,
gps: {
latitude,
longitude
}
}
})
}

39
gatsby/onCreateNode.js Normal file
View File

@ -0,0 +1,39 @@
const path = require('path')
const { createFilePath } = require('gatsby-source-filesystem')
// Create slug & date for posts from file path values
exports.createMarkdownFields = (node, createNodeField, getNode) => {
const fileNode = getNode(node.parent)
const parsedFilePath = path.parse(fileNode.relativePath)
const slugOriginal = createFilePath({ node, getNode })
// slug
let slug
if (parsedFilePath.name === 'index') {
slug = `/${parsedFilePath.dir.substring(11)}` // remove date from file dir
} else {
slug = `/${slugOriginal.substring(12)}` // remove first slash & date from file path
}
createNodeField({
node,
name: 'slug',
value: slug
})
// date
let date
if (node.frontmatter.date) {
date = `${node.frontmatter.date}`
} else {
date = `${slugOriginal.substring(1, 10)}` // grab date from file path
}
createNodeField({
node,
name: 'date',
value: date
})
}