You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

256 lines
6.9 KiB

/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
const {resolve} = require('path');
const webpack = require('webpack');
exports.modifyWebpackConfig = ({config, stage}) => {
// See https://github.com/FormidableLabs/react-live/issues/5
config.plugin('ignore', () => new webpack.IgnorePlugin(/^(xor|props)$/));
config.merge({
resolve: {
root: resolve(__dirname, './src'),
extensions: ['', '.js', '.jsx', '.json'],
},
});
return config;
};
exports.createPages = async ({graphql, boundActionCreators}) => {
const {createPage, createRedirect} = boundActionCreators;
// Used to detect and prevent duplicate redirects
const redirectToSlugMap = {};
const blogTemplate = resolve('./src/templates/blog.js');
const communityTemplate = resolve('./src/templates/community.js');
const docsTemplate = resolve('./src/templates/docs.js');
const tutorialTemplate = resolve('./src/templates/tutorial.js');
const homeTemplate = resolve('./src/templates/home.js');
const allMarkdown = await graphql(
`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
fields {
redirect
slug
}
}
}
}
}
`,
);
if (allMarkdown.errors) {
console.error(allMarkdown.errors);
throw Error(allMarkdown.errors);
}
allMarkdown.data.allMarkdownRemark.edges.forEach(edge => {
const slug = edge.node.fields.slug;
if (slug === '/index.html') {
createPage({
path: '/',
component: homeTemplate,
context: {
slug,
},
});
} else if (slug === 'docs/error-decoder.html') {
// No-op so far as markdown templates go.
// Error codes are managed by a page in src/pages
// (which gets created by Gatsby during a separate phase).
} else if (
slug.includes('blog/') ||
slug.includes('community/') ||
slug.includes('contributing/') ||
slug.includes('docs/') ||
slug.includes('tutorial/') ||
slug.includes('warnings/')
) {
let template;
if (slug.includes('blog/')) {
template = blogTemplate;
} else if (slug.includes('community/')) {
template = communityTemplate;
} else if (
slug.includes('contributing/') ||
slug.includes('docs/') ||
slug.includes('warnings/')
) {
template = docsTemplate;
} else if (slug.includes('tutorial/')) {
template = tutorialTemplate;
}
const createArticlePage = path =>
createPage({
path: path,
component: template,
context: {
slug,
},
});
// Register primary URL.
createArticlePage(slug);
// Register redirects as well if the markdown specifies them.
if (edge.node.fields.redirect) {
let redirect = JSON.parse(edge.node.fields.redirect);
if (!Array.isArray(redirect)) {
redirect = [redirect];
}
redirect.forEach(fromPath => {
if (redirectToSlugMap[fromPath] != null) {
console.error(`Duplicate redirect detected from "${fromPath}" to:\n` +
`* ${redirectToSlugMap[fromPath]}\n` +
`* ${slug}\n`
);
process.exit(1);
}
redirectToSlugMap[fromPath] = slug;
createRedirect({
fromPath: `/${fromPath}`,
redirectInBrowser: true,
toPath: `/${slug}`,
});
});
}
}
});
const newestBlogEntry = await graphql(
`
{
allMarkdownRemark(
limit: 1,
filter: { id: { regex: "/blog/" } }
sort: { fields: [fields___date], order: DESC }
) {
edges {
node {
fields {
slug
}
}
}
}
}
`,
);
const newestBlogNode = newestBlogEntry.data.allMarkdownRemark.edges[0].node;
// Blog landing page should always show the most recent blog entry.
createRedirect({
fromPath: '/blog/',
redirectInBrowser: true,
toPath: newestBlogNode.fields.slug,
});
};
// Parse date information out of blog post filename.
const BLOG_POST_FILENAME_REGEX = /([0-9]+)\-([0-9]+)\-([0-9]+)\-(.+)\.md$/;
// Add custom fields to MarkdownRemark nodes.
exports.onCreateNode = ({node, boundActionCreators, getNode}) => {
const {createNodeField} = boundActionCreators;
switch (node.internal.type) {
case 'MarkdownRemark':
const {permalink, redirect_from} = node.frontmatter;
const {relativePath} = getNode(node.parent);
let slug = permalink;
if (!slug) {
if (relativePath.includes('blog')) {
// Blog posts don't have embedded permalinks.
// Their slugs follow a pattern: /blog/<year>/<month>/<day>/<slug>.html
// The date portion comes from the file name: <date>-<title>.md
const match = BLOG_POST_FILENAME_REGEX.exec(relativePath);
const year = match[1];
const month = match[2];
const day = match[3];
const filename = match[4];
slug = `/blog/${year}/${month}/${day}/${filename}.html`;
const date = new Date(year, month - 1, day);
// Blog posts are sorted by date and display the date in their header.
createNodeField({
node,
name: 'date',
value: date.toJSON(),
});
}
}
if (!slug) {
slug = `/${relativePath.replace('.md', '.html')}`;
// This should (probably) only happen for the index.md,
// But let's log it in case it happens for other files also.
console.warn(
`Warning: No slug found for "${relativePath}". Falling back to default "${slug}".`,
);
}
// Used to generate URL to view this content.
createNodeField({
node,
name: 'slug',
value: slug,
});
// Used to generate a GitHub edit link.
createNodeField({
node,
name: 'path',
value: relativePath,
});
// Used by createPages() above to register redirects.
createNodeField({
node,
name: 'redirect',
value: redirect_from ? JSON.stringify(redirect_from) : '',
});
return;
}
};
exports.onCreatePage = async ({page, boundActionCreators}) => {
const {createPage} = boundActionCreators;
return new Promise(resolvePromise => {
// page.matchPath is a special key that's used for matching pages only on the client.
// Explicitly wire up all error code wildcard matches to redirect to the error code page.
if (page.path.includes('docs/error-decoder.html')) {
page.matchPath = 'docs/error-decoder:path?';
page.context.slug = 'docs/error-decoder.html';
createPage(page);
}
resolvePromise();
});
};