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:
parent
ab00dd2a32
commit
a502952403
@ -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
|
||||
|
||||
|
205
gatsby-node.js
205
gatsby-node.js
@ -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
81
gatsby/createPages.js
Normal 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
77
gatsby/exif.js
Normal 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
39
gatsby/onCreateNode.js
Normal 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
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue
Block a user