Source: gatsby-node.js

require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` })
const path = require("path")
const fs = require("fs")
const moment = require("moment-timezone")
const { fromUnixTime } = require("date-fns")

/**
 * A module to generate pages from the data from source
 *
 * @module createPages
 * @param {Object} actions - Object that holds gatsby method for managing nodes.
 * @param {function} createPage - Function from the actions object, used to create pages. {@link https://www.gatsbyjs.com/docs/reference/config-files/actions/#createNode|Gatsby Doc : createNode}
 * @param {function} graphql - graphql function to query data from internal data source.
 *
 * @see {@link https://www.gatsbyjs.com/docs/tutorial/part-seven/|Learn more about gatsby page generation system}
 */

exports.createPages = async ({
  actions: { createPage, createRedirect },
  graphql,
}) => {
  const maxPublicationDate = process.env.GATSBY_PUBLICATION_DATE
    ? moment
        .utc(fromUnixTime(process.env.GATSBY_PUBLICATION_DATE))
        .tz("Europe/Paris")
    : moment.utc(new Date()).tz("Europe/Paris")

  console.log(
    `
      Timestamp variable env : ${process.env.GATSBY_PUBLICATION_DATE}<br />
      Date actuelle : ${moment.utc(new Date()).tz("Europe/Paris")}<br />
      Date variable env : ${moment
        .utc(fromUnixTime(process.env.GATSBY_PUBLICATION_DATE))
        .tz("Europe/Paris")}<br />
      Date filtrage documents : ${maxPublicationDate}<br />
      Test : ${moment(maxPublicationDate).format("YYYYMMDDhhmmss")}
    `
  )
  const slugify = str => {
    const a =
      "àáâäæãåāăąçćčđďèéêëēėęěğǵḧîïíīįìłḿñńǹňôöòóœøōõṕŕřßśšşșťțûüùúūǘůűųẃẍÿýžźż·/_,:;"
    const b =
      "aaaaaaaaaacccddeeeeeeeegghiiiiiilmnnnnooooooooprrsssssttuuuuuuuuuwxyyzzz------"
    const p = new RegExp(a.split("").join("|"), "g")
    // AJOUTER LES STOP WORDS DE L'ANCIENNE BORNE

    return str
      .toString()
      .toLowerCase()
      .replace(/\s+/g, "-") // Replace spaces with -
      .replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters
      .replace(/&/g, "-and-") // Replace & with 'and'
      .replace(/[^\w\-]+/g, "") // Remove all non-word characters
      .replace(/\-\-+/g, "-") // Replace multiple - with single -
      .replace(/^-+/, "") // Trim - from start of text
      .replace(/-+$/, "") // Trim - from end of text
  }

  /**
   * Pulling data from gatsby internal source system
   *
   * @function graphql
   * @param {string} - Template littaral to describe the query
   * @async
   * @returns {Array}
   */
  await graphql(`
    {
      allPage {
        nodes {
          id
          fr
          en
          uuid
          type
        }
      }
      allDocumentCategories {
        nodes {
          id
          fr
          en
        }
      }
      allDocument {
        nodes {
          en
          fr
          id
          uuid
        }
      }
      allPageRse {
        nodes {
          fr
          en
          id
          uuid
          type
        }
      }
      allMenuPrincipal {
        nodes {
          fr
          id
          uuid
        }
      }
      allMenuPrincipalEn {
        nodes {
          en
          id
          uuid
        }
      }
    }
  `).then(result => {
    if (result.errors) {
      Promise.reject(result.errors)
    }

    const langs = ["fr", "en"]

    const allMenuParsed = result.data.allMenuPrincipal.nodes.map(node => {
      return {
        ...node,
        fr: node.fr ? JSON.parse(node.fr) : null,
      }
    })
    const allMenuEnParsed = result.data.allMenuPrincipalEn.nodes.map(node => {
      return {
        ...node,
        en: node.en ? JSON.parse(node.en) : null,
      }
    })
    const allPageParsed = result.data.allPage.nodes.map(node => {
      return {
        ...node,
        en: node.en ? JSON.parse(node.en) : null,
        fr: node.fr ? JSON.parse(node.fr) : null,
      }
    })
    const allPageRseParsed = result.data.allPageRse.nodes.map(node => {
      return {
        ...node,
        en: node.en ? JSON.parse(node.en) : null,
        fr: node.fr ? JSON.parse(node.fr) : null,
      }
    })

    const allDocumentParsed = result.data.allDocument.nodes.map(node => {
      return {
        ...node,
        type: "document",
        en: node.en ? JSON.parse(node.en) : null,
        fr: node.fr ? JSON.parse(node.fr) : null,
      }
    })

    /* REDIRECTS SSG */
    const pages = [...allPageParsed, ...allPageRseParsed, ...allDocumentParsed]
    const pagesRedirects = pages
      .map(node => {
        return langs
          .filter(lang => {
            return node[lang] && node[lang].alias
          })
          .filter(lang => {
            if (node.type === "document") {
              return (
                node[lang].document &&
                node[lang].document.file &&
                node[lang].document.file.url
              )
            } else {
              return node[lang].redirection && node[lang].redirection.length > 0
            }
          })
          .map(lang => {
            const alias = node[lang].alias
            const hasPrefix = alias.includes(`/${lang}/`) === true
            const uri = `${alias.replace(`/${lang}/`, "/")}/`

            if (node.type === "document") {
              return [
                {
                  source: `/${lang}${uri}`,
                  destination: node[lang].document.file.url,
                },
                {
                  source: uri,
                  destination: node[lang].document.file.url,
                },
              ]
            } else {
              /* pages */
              const redirects = node[lang].redirection
              return redirects.map(redirect => {
                /* Redirections drupal */
                return [
                  {
                    source:
                      `/${lang}` + redirect.replace(`/${lang}/`, "/") + "/",
                    destination: `/${lang}${uri}`,
                  },
                  {
                    source: `${redirect}/`,
                    destination: `/${lang}${uri}`,
                  },
                ]
              })
            }
          })
      })
      .flat()
      .flat()
      .flat()

    fs.writeFile(
      "redirects.json",
      JSON.stringify([
        ...pagesRedirects,
        {
          source: "/fr/",
          destination: "/",
        },
        {
          source: "/recherche/",
          destination: "/fr/recherche/",
        },
        {
          source: "/search/",
          destination: "/en/search/",
        },
      ]),
      err => {
        if (err) console.log(err)
        console.log(`${pagesRedirects.length} redirections created !`)
      }
    )

    /* REDIRECTS SSR */
    if (process.env.NODE_ENV === "development") {
      console.log("Creating SSR redirects...")
      langs.map(lang => {
        allDocumentParsed
          .filter(node => {
            return (
              node[lang] &&
              node[lang].alias &&
              node[lang].document &&
              node[lang].document.file &&
              node[lang].document.file.url
            )
          })
          .map(node => {
            createPage({
              path: `${node[lang].alias}/`,
              component: path.resolve(`src/templates/redirect.js`),
              context: {
                destination: node[lang].document.file.url,
              },
            })
            createPage({
              path: `/${lang}${node[lang].alias}/`,
              component: path.resolve(`src/templates/redirect.js`),
              context: {
                destination: node[lang].document.file.url,
              },
            })
          })
      })
    }

    /**
     * After getting the data from graphql and reorganizing it in the desired shape,
     *
     * createPage is used to generate the static html file using the defined react template.
     *
     * @function createPage
     * @param {Object} node - Object containing data to create the page
     * @property {string} path - Url path used for the page being created
     * @property {string} component - Path to the react component used as template
     * @property {Object} context - Contains all the data relative to the page
     */

    /* PAGES */
    const allPages = [...allPageParsed, ...allPageRseParsed]
    allPages.map(node => {
      return langs.map(lang => {
        if (
          node[lang] &&
          node[lang].title &&
          node[lang].type !== "homepage" &&
          node[lang].alias
        ) {
          if (
            process.env.NODE_ENV === "development" ||
            (process.env.NODE_ENV === "production" &&
              node[lang].status === true)
          ) {
            return createPage({
              path: `/${lang}${node[lang].alias}`,
              component: path.resolve(`src/templates/page.js`),
              context: {
                lang,
                id: node.id,
                uuid: node.uuid,
                title: node[lang].title,
                en: node.en ? node.en : null,
                fr: node.fr ? node.fr : null,
                pageType:
                  node.type === "page"
                    ? node[lang].type
                    : node[lang].is_index
                    ? "homepageRse"
                    : "rse",
                allMenuFr: allMenuParsed,
                allMenuEn: allMenuEnParsed,
              },
            })
          }
        }
      })
    })
  })
}