gatsby-theme-guitar-book
Version:
A Gatsby theme for building guitar book websites
365 lines (323 loc) • 8.75 kB
JavaScript
const jsYaml = require('js-yaml');
const path = require('path');
const git = require('simple-git')();
const {createFilePath} = require('gatsby-source-filesystem');
const {getVersionBasePath} = require('./src/utils');
const {createPrinterNode} = require('gatsby-plugin-printer');
function getConfigPaths(baseDir) {
return [
path.join(baseDir, 'gatsby-config.js'), // new gatsby config
path.join(baseDir, '_config.yml') // old hexo config
];
}
async function onCreateNode(
{node, actions, getNode, loadNodeContent},
{
baseDir = '',
contentDir = 'content',
defaultVersion = 'default',
localVersion,
siteName,
subtitle,
sidebarCategories
}
) {
const configPaths = getConfigPaths(baseDir);
if (configPaths.includes(node.relativePath)) {
const value = await loadNodeContent(node);
actions.createNodeField({
name: 'raw',
node,
value
});
}
if (['MarkdownRemark', 'Mdx'].includes(node.internal.type)) {
const parent = getNode(node.parent);
let version = localVersion || defaultVersion;
let slug = createFilePath({
node,
getNode
});
if (node.frontmatter.slug) {
slug = node.frontmatter.slug; // eslint-disable-line prefer-destructuring
}
let category;
const fileName = parent.name;
const outputDir = 'social-cards';
for (const key in sidebarCategories) {
if (key !== 'null') {
const categories = sidebarCategories[key];
const trimmedSlug = slug.replace(/^\/|\/$/g, '');
if (categories.includes(trimmedSlug)) {
category = key;
break;
}
}
}
const {title, sidebar_title, graphManagerUrl} = node.frontmatter;
createPrinterNode({
id: `${node.id} >>> Printer`,
fileName,
outputDir,
data: {
title,
subtitle: subtitle || siteName,
category
},
component: require.resolve('./src/components/social-card.js')
});
actions.createNodeField({
name: 'image',
node,
value: path.join(outputDir, fileName + '.png')
});
let versionRef = '';
if (parent.gitRemote___NODE) {
const gitRemote = getNode(parent.gitRemote___NODE);
version = gitRemote.sourceInstanceName;
versionRef = gitRemote.ref;
const dirPattern = new RegExp(path.join('^', baseDir, contentDir));
slug = slug.replace(dirPattern, '');
}
if (version !== defaultVersion) {
slug = getVersionBasePath(version) + slug;
}
actions.createNodeField({
node,
name: 'version',
value: version
});
actions.createNodeField({
node,
name: 'versionRef',
value: versionRef
});
actions.createNodeField({
node,
name: 'slug',
value: slug
});
actions.createNodeField({
node,
name: 'sidebarTitle',
value: sidebar_title || ''
});
actions.createNodeField({
node,
name: 'graphManagerUrl',
value: graphManagerUrl || ''
});
}
}
exports.onCreateNode = onCreateNode;
function getPageFromEdge({node}) {
return node.childMarkdownRemark || node.childMdx;
}
function getSidebarContents(sidebarCategories, edges, version, dirPattern) {
return Object.keys(sidebarCategories).map(key => ({
title: key === 'null' ? null : key,
pages: sidebarCategories[key]
.map(linkPath => {
const match = linkPath.match(/^\[(.+)\]\((https?:\/\/.+)\)$/);
if (match) {
return {
anchor: true,
title: match[1],
path: match[2]
};
}
const edge = edges.find(edge => {
const {relativePath} = edge.node;
const {fields} = getPageFromEdge(edge);
return (
fields.version === version &&
relativePath
.slice(0, relativePath.lastIndexOf('.'))
.replace(dirPattern, '') === linkPath
);
});
if (!edge) {
return null;
}
const {frontmatter, fields} = getPageFromEdge(edge);
return {
title: frontmatter.title,
sidebarTitle: fields.sidebarTitle,
description: frontmatter.description,
path: fields.slug
};
})
.filter(Boolean)
}));
}
function getVersionSidebarCategories(gatsbyConfig, hexoConfig) {
if (gatsbyConfig) {
const trimmed = gatsbyConfig.slice(
gatsbyConfig.indexOf('sidebarCategories')
);
const json = trimmed
.slice(0, trimmed.indexOf('}'))
// wrap object keys in double quotes
.replace(/['"]?(\w[\w\s&-]+)['"]?:/g, '"$1":')
// replace single-quoted array values with double quoted ones
.replace(/'([\w-/.]+)'/g, '"$1"')
// remove trailing commas
.trim()
.replace(/,\s*\]/g, ']')
.replace(/,\s*$/, '');
const {sidebarCategories} = JSON.parse(`{${json}}}`);
return sidebarCategories;
}
const {sidebar_categories} = jsYaml.load(hexoConfig);
return sidebar_categories;
}
const pageFragment = `
internal {
type
}
frontmatter {
title
description
}
fields {
slug
version
versionRef
sidebarTitle
}
`;
exports.createPages = async (
{actions, graphql},
{
baseDir = '',
contentDir = 'content',
defaultVersion = 'default',
versions = {},
subtitle,
githubRepo,
sidebarCategories,
twitterHandle,
adSense,
localVersion,
baseUrl
}
) => {
const {data} = await graphql(`
{
allFile(filter: {extension: {in: ["md", "mdx"]}}) {
edges {
node {
id
relativePath
childMarkdownRemark {
${pageFragment}
}
childMdx {
${pageFragment}
}
}
}
}
}
`);
const {edges} = data.allFile;
const mainVersion = localVersion || defaultVersion;
const contentPath = path.join(baseDir, contentDir);
const dirPattern = new RegExp(`^${contentPath}/`);
const sidebarContents = {
[mainVersion]: getSidebarContents(
sidebarCategories,
edges,
mainVersion,
dirPattern
)
};
const configPaths = getConfigPaths(baseDir);
const versionKeys = [localVersion].filter(Boolean);
for (const version in versions) {
if (version !== defaultVersion) {
versionKeys.push(version);
}
// grab the old config files for each older version
const configs = await Promise.all(
configPaths.map(async configPath => {
const response = await graphql(`
{
file(
relativePath: {eq: "${configPath}"}
gitRemote: {sourceInstanceName: {eq: "${version}"}}
) {
fields {
raw
}
}
}
`);
const {file} = response.data;
return file && file.fields.raw;
})
);
sidebarContents[version] = getSidebarContents(
getVersionSidebarCategories(...configs),
edges,
version,
dirPattern
);
}
let defaultVersionNumber = null;
try {
defaultVersionNumber = parseFloat(defaultVersion, 10);
} catch (error) {
// let it slide
}
// get the current git branch
// try to use the BRANCH env var from Netlify
// fall back to using git rev-parse if BRANCH is not available
const currentBranch =
process.env.BRANCH || (await git.revparse(['--abbrev-ref', 'HEAD']));
const template = require.resolve('./src/components/template');
edges.forEach(edge => {
const {id, relativePath} = edge.node;
const {fields} = getPageFromEdge(edge);
let versionDifference = 0;
if (defaultVersionNumber) {
try {
const versionNumber = parseFloat(fields.version, 10);
versionDifference = versionNumber - defaultVersionNumber;
} catch (error) {
// do nothing
}
}
let githubUrl;
if (githubRepo) {
const [owner, repo] = githubRepo.split('/');
githubUrl =
'https://' +
path.join(
'github.com',
owner,
repo,
'tree',
fields.versionRef || path.join(currentBranch, contentPath),
relativePath
);
}
actions.createPage({
path: fields.slug,
component: template,
context: {
id,
subtitle,
versionDifference,
versionBasePath: getVersionBasePath(fields.version),
sidebarContents: sidebarContents[fields.version],
githubUrl,
twitterHandle,
adSense,
versions: versionKeys, // only need to send version labels to client
defaultVersion,
baseUrl
}
});
});
};