diff --git a/config.js b/config.js
index d41211a32..9688bd476 100644
--- a/config.js
+++ b/config.js
@@ -21,4 +21,9 @@ module.exports = {
moduleName: `_30s`,
rollupInputFile: `imports.temp.js`,
testModuleFile: `test/_30s.js`,
+ // Requirable JSONs
+ requirables: [
+ `snippets.json`,
+ `archivedSnippets.json`
+ ]
};
diff --git a/gatsby-node.js b/gatsby-node.js
index 9e8c21e72..570b87f07 100644
--- a/gatsby-node.js
+++ b/gatsby-node.js
@@ -2,6 +2,14 @@ const path = require(`path`);
const { createFilePath } = require(`gatsby-source-filesystem`);
const config = require('./config');
+const { getTextualContent, getCodeBlocks, optimizeAllNodes } = require(`./src/docs/util`);
+
+const requirables = [];
+
+config.requirables.forEach(fileName => {
+ requirables.push(require(`./snippet_data/${fileName}`));
+});
+
const toKebabCase = str =>
str &&
str
@@ -9,97 +17,6 @@ const toKebabCase = str =>
.map(x => x.toLowerCase())
.join('-');
-exports.createPages = ({ graphql, actions }) => {
- const { createPage } = actions;
-
- const snippetPage = path.resolve(`./src/docs/templates/SnippetPage.js`);
- const tagPage = path.resolve(`./src/docs/templates/TagPage.js`);
- return graphql(
- `
- {
- allMarkdownRemark(
- sort: { fields: [frontmatter___title], order: ASC }
- limit: 1000
- ) {
- edges {
- node {
- fields {
- slug
- }
- frontmatter {
- tags
- }
- fileAbsolutePath
- }
- }
- }
- }
- `,
- ).then(result => {
- if (result.errors) {
- throw result.errors;
- }
-
- // Create individual snippet pages.
- const snippets = result.data.allMarkdownRemark.edges;
-
- snippets.forEach((post, index) => {
- if(post.node.fileAbsolutePath.indexOf('README') !== -1)
- return;
- if (post.node.fileAbsolutePath.indexOf(config.snippetArchivePath) === -1)
- createPage({
- path: `/snippet${post.node.fields.slug}`,
- component: snippetPage,
- context: {
- slug: post.node.fields.slug,
- scope: `./snippets`,
- },
- });
- else
- createPage({
- path: `/archive${post.node.fields.slug}`,
- component: snippetPage,
- context: {
- slug: post.node.fields.slug,
- scope: `./snippets_archive`,
- },
- });
- });
-
- // Create tag pages.
- const tags = snippets.reduce((acc, post) => {
- if (!post.node.frontmatter || !post.node.frontmatter.tags) return acc;
- const primaryTag = post.node.frontmatter.tags.split(',')[0];
- if (!acc.includes(primaryTag)) acc.push(primaryTag);
- return acc;
- }, []);
-
- tags.forEach(tag => {
- const tagPath = `/tag/${toKebabCase(tag)}/`;
- const tagRegex = `/^\\s*${tag}/`;
- createPage({
- path: tagPath,
- component: tagPage,
- context: {
- tag,
- tagRegex,
- },
- });
- });
-
- createPage({
- path: `/beginner`,
- component: tagPage,
- context: {
- tag: `beginner snippets`,
- tagRegex: `/beginner/`,
- },
- });
-
- return null;
- });
-};
-
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions;
@@ -112,3 +29,231 @@ exports.onCreateNode = ({ node, actions, getNode }) => {
});
}
};
+
+exports.sourceNodes = ({ actions, createNodeId, createContentDigest, getNodesByType }) => {
+ const { createTypes, createNode } = actions;
+ const typeDefs = `
+ type Snippet implements Node {
+ html: HtmlData
+ tags: TagData
+ title: String
+ code: CodeData
+ id: String
+ slug: String
+ path: String
+ text: TextData
+ archived: Boolean
+ }
+
+ type HtmlData @infer {
+ full: String
+ text: String
+ code: String
+ example: String
+ }
+
+ type CodeData @infer {
+ src: String
+ example: String
+ }
+
+ type TextData @infer {
+ full: String
+ short: String
+ }
+
+ type TagData @infer {
+ primary: String
+ all: [String]
+ }
+ `;
+ createTypes(typeDefs);
+
+ const markdownNodes = getNodesByType('MarkdownRemark');
+
+ const snippetNodes = requirables
+ .reduce((acc, sArr) => {
+ const archivedScope = sArr.meta.scope.indexOf('archive') !== -1;
+ return ({
+ ...acc,
+ ...sArr.data.reduce((snippets, snippet) => {
+ return ({
+ ...snippets,
+ [snippet.id]: { ...snippet, archived: archivedScope}
+ });
+ }, {})
+ });
+ }, {});
+
+ Object.entries(snippetNodes).forEach(([id, sNode]) => {
+ let mNode = markdownNodes.find(mN => mN.frontmatter.title === id);
+ let nodeContent = {
+ id,
+ tags: {
+ all: sNode.attributes.tags,
+ primary: sNode.attributes.tags[0]
+ },
+ title: mNode.frontmatter.title,
+ code: {
+ src: sNode.attributes.codeBlocks.es6,
+ example: sNode.attributes.codeBlocks.example
+ },
+ slug: mNode.fields.slug,
+ path: mNode.fileAbsolutePath,
+ text: {
+ full: sNode.attributes.text,
+ short: sNode.attributes.text.slice(0, sNode.attributes.text.indexOf('\n\n'))
+ },
+ archived: sNode.archived
+ };
+ createNode({
+ id: createNodeId(`snippet-${sNode.meta.hash}`),
+ parent: null,
+ children: [],
+ internal: {
+ type: 'Snippet',
+ content: JSON.stringify(nodeContent),
+ contentDigest: createContentDigest(nodeContent)
+ },
+ ...nodeContent
+ });
+ });
+
+};
+
+exports.createResolvers = ({ createResolvers }) => createResolvers({
+ Snippet: {
+ html: {
+ resolve: async (source, _, context, info) => {
+ const resolver = info.schema.getType("MarkdownRemark").getFields()["html"].resolve;
+ const node = await context.nodeModel.nodeStore.getNodesByType('MarkdownRemark').filter(v => v.frontmatter.title === source.title)[0];
+ const args = {}; // arguments passed to the resolver
+ const html = await resolver(node, args);
+ return {
+ full: `${html}`,
+ text: `${getTextualContent(html, true)}`,
+ code: `${optimizeAllNodes(getCodeBlocks(html).code)}`,
+ example: `${optimizeAllNodes(getCodeBlocks(html).example)}`
+ };
+ }
+ }
+ }
+});
+
+exports.createPages = ({ graphql, actions }) => {
+ const { createPage } = actions;
+
+ const snippetPage = path.resolve(`./src/docs/templates/SnippetPage.js`);
+ const tagPage = path.resolve(`./src/docs/templates/TagPage.js`);
+
+ return graphql(
+ `
+ {
+ allSnippet(sort: {fields: id}) {
+ edges {
+ node {
+ id
+ slug
+ tags {
+ all
+ primary
+ }
+ text {
+ full
+ short
+ }
+ title
+ html {
+ code
+ example
+ full
+ text
+ }
+ code {
+ src
+ example
+ }
+ archived
+ }
+ }
+ }
+ }
+ `,
+ ).then(result => {
+ if (result.errors) {
+ throw result.errors;
+ }
+
+ // Create individual snippet pages.
+ const snippets = result.data.allSnippet.edges;
+
+ snippets.forEach(snippet => {
+ if (!snippet.node.archived) {
+ createPage({
+ path: `/snippet${snippet.node.slug}`,
+ component: snippetPage,
+ context: {
+ snippet: snippet.node
+ }
+ });
+ } else {
+ createPage({
+ path: `/archive${snippet.node.slug}`,
+ component: snippetPage,
+ context: {
+ snippet: snippet.node
+ }
+ });
+ }
+ });
+
+ // Create tag pages.
+ const tags = [...new Set(
+ snippets.map(snippet => (snippet.node.tags || {primary: null}).primary)
+ )]
+ .filter(Boolean)
+ .sort((a, b) => a.localeCompare(b));
+
+ tags.forEach(tag => {
+ const tagPath = `/tag/${toKebabCase(tag)}/`;
+ const taggedSnippets = snippets
+ .filter(snippet => snippet.node.tags.primary === tag)
+ .filter(snippet => !snippet.node.archived)
+ .map(({node}) => ({
+ title: node.title,
+ html: node.html.text,
+ tags: node.tags.all,
+ id: node.slug.slice(1)
+ }));
+ createPage({
+ path: tagPath,
+ component: tagPage,
+ context: {
+ tag,
+ snippets: taggedSnippets
+ },
+ });
+ });
+
+ const beginnerSnippets = snippets
+ .filter(({ node }) => node.tags.all.includes('beginner'))
+ .filter(snippet => !snippet.node.archived)
+ .map(({ node }) => ({
+ title: node.title,
+ html: node.html.text,
+ tags: node.tags.all,
+ id: node.slug.slice(1)
+ }));
+
+ createPage({
+ path: `/beginner`,
+ component: tagPage,
+ context: {
+ tag: `beginner snippets`,
+ snippets: beginnerSnippets
+ },
+ });
+
+ return null;
+ });
+};
diff --git a/src/docs/components/SnippetCard.js b/src/docs/components/SnippetCard.js
index e2088ec20..071ac8a97 100644
--- a/src/docs/components/SnippetCard.js
+++ b/src/docs/components/SnippetCard.js
@@ -42,24 +42,18 @@ const CardCorner = ({ difficulty = 'intermediate' }) => (
// ===================================================
const FullCard = ({ snippetData, difficulty, isDarkMode }) => {
const [examplesOpen, setExamplesOpen] = React.useState(false);
- const tags = snippetData.tags;
- let cardCodeHtml = `${optimizeAllNodes(
- getCodeBlocks(snippetData.html).code,
- )}`;
- let cardExamplesHtml = `${optimizeAllNodes(
- getCodeBlocks(snippetData.html).example,
- )}`;
+
return (
{snippetData.title}
- {tags.map(tag => (
+ {snippetData.tags.map(tag => (
{tag}
))}
@@ -83,12 +77,9 @@ const FullCard = ({ snippetData, difficulty, isDarkMode }) => {
aria-label='Copy to clipboard'
/>
- {/*
-
- */}
{
{examplesOpen && (
)}
@@ -124,13 +115,12 @@ const ShortCard = ({
diff --git a/src/docs/pages/archive.js b/src/docs/pages/archive.js
index b48aa13e8..12814f0bb 100644
--- a/src/docs/pages/archive.js
+++ b/src/docs/pages/archive.js
@@ -11,7 +11,7 @@ import SnippetCard from '../components/SnippetCard'
// Individual snippet category/tag page
// ===================================================
const ArchivePage = props => {
- const posts = props.data.allMarkdownRemark.edges;
+ const snippets = props.data.allSnippet.edges;
React.useEffect(() => {
props.dispatch(pushNewPage('Archived', props.path));
@@ -24,17 +24,17 @@ const ArchivePage = props => {
Archived snippets
These snippets, while useful and interesting, didn't quite make it into the repository due to either having very specific use-cases or being outdated. However we felt like they might still be useful to some readers, so here they are.
Click on a snippet card to view the snippet.
- {posts &&
- posts.map(({ node }) => (
+ {snippets &&
+ snippets.map(({ node }) => (
v.trim()),
- id: node.fields.slug.slice(1),
+ title: node.title,
+ html: node.html.text,
+ tags: node.tags.all,
+ id: node.id
}}
isDarkMode={props.isDarkMode}
/>
@@ -55,25 +55,19 @@ export default connect(
)(ArchivePage);
export const archivePageQuery = graphql`
- query ArchivePage {
- allMarkdownRemark(
- limit: 1000
- sort: { fields: [frontmatter___title], order: ASC }
- filter: { fileAbsolutePath: { regex: "/snippets_archive/" }, frontmatter: {title: { ne: "" } } }
- ) {
- totalCount
+ query archiveListing {
+ allSnippet(filter: {archived: {eq: true}}) {
edges {
node {
+ title
+ html {
+ text
+ }
+ tags {
+ all
+ primary
+ }
id
- html
- rawMarkdownBody
- fields {
- slug
- }
- frontmatter {
- title
- tags
- }
}
}
}
diff --git a/src/docs/pages/index.js b/src/docs/pages/index.js
index fbeff5b2d..a13bd4aae 100644
--- a/src/docs/pages/index.js
+++ b/src/docs/pages/index.js
@@ -13,14 +13,7 @@ import SimpleCard from '../components/SimpleCard';
// Home page (splash and search)
// ===================================================
const IndexPage = props => {
- const snippets = props.data.snippetDataJson.data.map(snippet => ({
- title: snippet.title,
- html: props.data.allMarkdownRemark.edges.find(
- v => v.node.frontmatter.title === snippet.title,
- ).node.html,
- tags: snippet.attributes.tags,
- id: snippet.id
- }));
+ const snippets = props.data.allSnippet.edges;
const [searchQuery, setSearchQuery] = React.useState(props.searchQuery);
const [searchResults, setSearchResults] = React.useState(snippets);
@@ -31,9 +24,9 @@ const IndexPage = props => {
let results = snippets;
if (q.trim().length)
results = snippets.filter(
- v =>
- v.tags.filter(t => t.indexOf(q) !== -1).length ||
- v.title.toLowerCase().indexOf(q) !== -1,
+ ({node}) =>
+ node.tags.all.filter(t => t.indexOf(q) !== -1).length ||
+ node.title.toLowerCase().indexOf(q) !== -1,
);
setSearchResults(results);
}, [searchQuery]);
@@ -80,11 +73,16 @@ const IndexPage = props => {
Click on a snippet card to view the snippet.
Search results
- {searchResults.map(snippet => (
+ {searchResults.map(({node}) => (
))}
@@ -139,22 +137,17 @@ export const indexPageQuery = graphql`
}
}
}
- snippetDataJson(meta: { type: { eq: "snippetListingArray" }, scope: {eq: "./snippets"} }) {
- data {
- id
- title
- attributes {
- tags
- }
- }
- }
- allMarkdownRemark {
+ allSnippet {
edges {
node {
- html
- frontmatter {
- title
+ title
+ html {
+ text
}
+ tags {
+ all
+ }
+ id
}
}
}
diff --git a/src/docs/pages/list.js b/src/docs/pages/list.js
index 18e0dfd43..fb7b462aa 100644
--- a/src/docs/pages/list.js
+++ b/src/docs/pages/list.js
@@ -15,28 +15,15 @@ import SimpleCard from '../components/SimpleCard';
// Snippet list page
// ===================================================
const ListPage = props => {
- const snippets = props.data.snippetDataJson.data.map(snippet => ({
- title: snippet.title,
- html: props.data.allMarkdownRemark.edges.find(
- v => v.node.frontmatter.title === snippet.title,
- ).node.html,
- tags: snippet.attributes.tags,
- id: snippet.id,
- }));
- const archivedSnippets = props.data.snippetsArchiveDataJson.data.map(snippet => ({
- title: snippet.title,
- html: props.data.allMarkdownRemark.edges.find(
- v => v.node.frontmatter.title === snippet.title,
- ).node.html,
- tags: snippet.attributes.tags,
- id: snippet.id,
- }));
- const tags = snippets.reduce((acc, snippet) => {
- if (!snippet.tags) return acc;
- const primaryTag = snippet.tags[0];
- if (!acc.includes(primaryTag)) acc.push(primaryTag);
- return acc;
- }, []);
+ const snippets = props.data.allSnippet.edges;
+ const archivedSnippets = props.data.allArchivedSnippet.edges;
+
+ const tags = [...new Set(
+ snippets.map(snippet => (snippet.node.tags || { primary: null }).primary)
+ )]
+ .filter(Boolean)
+ .sort((a, b) => a.localeCompare(b));
+
const staticPages = [
{
url: 'beginner',
@@ -79,12 +66,17 @@ const ListPage = props => {
{snippets
- .filter(snippet => snippet.tags[0] === tag)
- .map(snippet => (
+ .filter(({node}) => node.tags.primary === tag)
+ .map(({node}) => (
))}
>
@@ -95,12 +87,17 @@ const ListPage = props => {
Archived snippets
{archivedSnippets
- .map(snippet => (
+ .map(({node}) => (
))}
@@ -131,31 +128,32 @@ export default connect(
export const listPageQuery = graphql`
query snippetListing {
- snippetDataJson(meta: { type: { eq: "snippetListingArray" }, scope: {eq: "./snippets"} }) {
- data {
- id
- title
- attributes {
- tags
- }
- }
- }
- snippetsArchiveDataJson : snippetDataJson(meta: { type: { eq: "snippetListingArray" }, scope: {eq: "./snippets_archive"} }) {
- data {
- id
- title
- attributes {
- tags
- }
- }
- }
- allMarkdownRemark {
+ allSnippet(filter: {archived: {eq: false}}) {
edges {
node {
- html
- frontmatter {
- title
+ title
+ html {
+ text
}
+ tags {
+ all
+ primary
+ }
+ id
+ }
+ }
+ }
+ allArchivedSnippet: allSnippet(filter: {archived: {eq: true}}) {
+ edges {
+ node {
+ title
+ html {
+ text
+ }
+ tags {
+ all
+ }
+ id
}
}
}
diff --git a/src/docs/pages/search.js b/src/docs/pages/search.js
index 7e582ce60..7f19d78a7 100644
--- a/src/docs/pages/search.js
+++ b/src/docs/pages/search.js
@@ -12,14 +12,7 @@ import SnippetCard from '../components/SnippetCard';
// Search page
// ===================================================
const SearchPage = props => {
- const snippets = props.data.snippetDataJson.data.map(snippet => ({
- title: snippet.title,
- html: props.data.allMarkdownRemark.edges.find(
- v => v.node.frontmatter.title === snippet.title,
- ).node.html,
- tags: snippet.attributes.tags,
- id: snippet.id,
- }));
+ const snippets = props.data.allSnippet.edges;
const [searchQuery, setSearchQuery] = React.useState(props.searchQuery);
const [searchResults, setSearchResults] = React.useState(snippets);
@@ -30,9 +23,9 @@ const SearchPage = props => {
let results = snippets;
if (q.trim().length)
results = snippets.filter(
- v =>
- v.tags.filter(t => t.indexOf(q) !== -1).length ||
- v.title.toLowerCase().indexOf(q) !== -1,
+ ({ node }) =>
+ node.tags.all.filter(t => t.indexOf(q) !== -1).length ||
+ node.title.toLowerCase().indexOf(q) !== -1,
);
setSearchResults(results);
}, [searchQuery]);
@@ -75,11 +68,16 @@ const SearchPage = props => {
) : (
<>
Search results
- {searchResults.map(snippet => (
+ {searchResults.map(({node}) => (
))}
@@ -102,22 +100,17 @@ export default connect(
export const searchPageQuery = graphql`
query searchSnippetList {
- snippetDataJson(meta: { type: { eq: "snippetListingArray" }, scope: {eq: "./snippets"} }) {
- data {
- id
- title
- attributes {
- tags
- }
- }
- }
- allMarkdownRemark {
+ allSnippet {
edges {
- node {
- html
- frontmatter {
- title
+ node {
+ title
+ html {
+ text
}
+ tags {
+ all
+ }
+ id
}
}
}
diff --git a/src/docs/templates/SnippetPage.js b/src/docs/templates/SnippetPage.js
index 996e996f8..7db74c381 100644
--- a/src/docs/templates/SnippetPage.js
+++ b/src/docs/templates/SnippetPage.js
@@ -11,14 +11,11 @@ import BackArrowIcon from '../components/SVGs/BackArrowIcon';
// Individual snippet page template
// ===================================================
const SnippetPage = props => {
- const post = props.data.markdownRemark;
- const postData = props.data.snippetDataJson.data.find(
- v => v.title === post.frontmatter.title,
- );
+ const snippet = props.pageContext.snippet;
return (
<>
-
+
{
@@ -50,54 +50,3 @@ export default connect(
}),
null,
)(SnippetPage);
-
-export const pageQuery = graphql`
- query BlogPostBySlug($slug: String!, $scope: String!) {
- logo: file(absolutePath: { regex: "/logo_reverse_md.png/" }) {
- id
- childImageSharp {
- fixed(height: 45, width: 45) {
- src
- }
- }
- }
- allMarkdownRemark {
- edges {
- node {
- fields {
- slug
- }
- fileAbsolutePath
- frontmatter {
- title
- }
- }
- }
- }
- markdownRemark(fields: { slug: { eq: $slug } }) {
- id
- fields {
- slug
- }
- excerpt(pruneLength: 160)
- html
- frontmatter {
- title
- }
- }
- snippetDataJson(meta: { type: { eq: "snippetArray" }, scope: {eq: $scope} }) {
- data {
- title
- id
- attributes {
- text
- codeBlocks {
- es6
- example
- }
- tags
- }
- }
- }
- }
-`;
diff --git a/src/docs/templates/TagPage.js b/src/docs/templates/TagPage.js
index 101605ad2..0c7a5a802 100644
--- a/src/docs/templates/TagPage.js
+++ b/src/docs/templates/TagPage.js
@@ -13,8 +13,8 @@ import { capitalize, getRawCodeBlocks as getCodeBlocks } from '../util';
// Individual snippet category/tag page
// ===================================================
const TagRoute = props => {
- const posts = props.data.allMarkdownRemark.edges;
const tag = props.pageContext.tag;
+ const snippets = props.pageContext.snippets;
React.useEffect(() => {
props.dispatch(pushNewPage(capitalize(tag), props.path));
@@ -26,16 +26,16 @@ const TagRoute = props => {
{capitalize(tag)}
Click on a snippet card to view the snippet.
- {posts &&
- posts.map(({ node }) => (
+ {snippets &&
+ snippets.map(snippet => (
v.trim()),
- id: node.fields.slug.slice(1),
+ title: snippet.title,
+ html: snippet.html,
+ tags: snippet.tags,
+ id: snippet.id,
}}
isDarkMode={props.isDarkMode}
/>
@@ -54,28 +54,3 @@ export default connect(
}),
null,
)(TagRoute);
-
-export const tagPageQuery = graphql`
- query TagPage($tagRegex: String) {
- allMarkdownRemark(
- limit: 1000
- sort: { fields: [frontmatter___title], order: ASC }
- filter: { fileAbsolutePath: { regex: "/snippets(?!_archive)/" }, frontmatter: { tags: { regex: $tagRegex } } }
- ) {
- totalCount
- edges {
- node {
- id
- html
- fields {
- slug
- }
- frontmatter {
- title
- tags
- }
- }
- }
- }
- }
-`;