Example #1
0
      .on(`all`, (event, path) => {
        if (event === `change` || event === `add`) {
          // Copy it over local version.

          // Don't copy over the Gatsby bin file as that breaks the NPM symlink.
          if (_.includes(path, `dist/gatsby-cli.js`)) {
            return
          }

          const newPath = syspath.join(
            `./node_modules/${p}`,
            syspath.relative(prefix, path)
          )

          copyPath(path, newPath, quiet)

          // If this is from "cache-dir" also copy it into the site's .cache
          if (_.includes(path, `cache-dir`)) {
            const newCachePath = syspath.join(
              `.cache/`,
              syspath.relative(syspath.join(prefix, `cache-dir`), path)
            )
            copyPath(path, newCachePath, quiet)
          }

          if (scanOnce) {
            debouncedQuit()
          }
        }
      })
Example #2
0
exports.createPageDependency = ({ path, nodeId, connection }) => {
  const state = store.getState()

  // Check that the dependencies aren't already recorded so we
  // can avoid creating lots of very noisy actions.
  let nodeDependencyExists = false
  let connectionDependencyExists = false
  if (!nodeId) {
    nodeDependencyExists = true
  }
  if (
    nodeId &&
    _.includes(state.componentDataDependencies.nodes[nodeId], path)
  ) {
    nodeDependencyExists = true
  }
  if (!connection) {
    connectionDependencyExists = true
  }
  if (
    connection &&
    _.includes(state.componentDataDependencies.connections, connection)
  ) {
    connectionDependencyExists = true
  }
  if (nodeDependencyExists && connectionDependencyExists) {
    return
  }

  // It's new, let's dispatch it
  const action = actions.createPageDependency({ path, nodeId, connection })
  store.dispatch(action)
}
Example #3
0
    (obj, next, key, po, pn, stack) => {
      if (obj === INVALID_VALUE) return obj

      // TODO: if you want to support infering Union types this should be handled
      // differently. Maybe merge all like types into examples for each type?
      // e.g. union: [1, { foo: true }, ['brown']] -> Union Int|Object|List
      if (!isSameType(obj, next)) return INVALID_VALUE

      if (!_.isArray(obj || next)) {
        // Prefer floats over ints as they're more specific.
        if (obj && _.isNumber(obj) && !_.isInteger(obj)) return obj
        if (obj === null) return next
        if (next === null) return obj
        return undefined
      }

      let array = [].concat(obj, next).filter(isDefined)

      if (!array.length) return null
      if (!areAllSameType(array)) return INVALID_VALUE

      // Linked node arrays don't get reduced further as we
      // want to preserve all the linked node types.
      if (_.includes(key, `___NODE`)) {
        return array
      }

      // primitive values don't get merged further, just take the first item
      if (!_.isObject(array[0])) return array.slice(0, 1)
      let merged = extractFieldExamples(array)
      return isDefined(merged) ? [merged] : null
    }
    _.each(clonedArgs.filter, function (v, k) {
      // Ignore connection and sorting args.
      if (_.includes([`skip`, `limit`, `sort`], k)) return;

      siftArgs.push(siftifyArgs({ [k]: v }));
      extractFieldsToSift(``, k, {}, fieldsToSift, v);
    });
  _.each(exampleValue, (value, key) => {
    // Remove fields common to the top-level of all nodes.  We add these
    // elsewhere so don't need to infer their type.
    if (isRoot && EXCLUDE_KEYS[key]) return

    // Several checks to see if a field is pointing to custom type
    // before we try automatic inference.
    const nextSelector = selector ? `${selector}.${key}` : key
    const fieldSelector = `${nodes[0].internal.type}.${nextSelector}`

    let fieldName = key
    let inferredField

    // First check for manual field => type mappings in the site's
    // gatsby-config.js
    if (mapping && _.includes(Object.keys(mapping), fieldSelector)) {
      inferredField = inferFromMapping(value, mapping, fieldSelector, types)

      // Second if the field has a suffix of ___node. We use then the value
      // (a node id) to find the node and use that node's type as the field
    } else if (_.includes(key, `___NODE`)) {
      ;[fieldName] = key.split(`___`)
      inferredField = inferFromFieldName(value, nextSelector, types)

      // Third if the field is pointing to a file (from another file).
    } else if (
      nodes[0].internal.type !== `File` &&
      shouldInferFile(nodes, nextSelector, value)
    ) {
      inferredField = inferFromUri(key, types)
    }

    // Finally our automatic inference of field value type.
    if (!inferredField) {
      inferredField = inferGraphQLType({
        nodes,
        types,
        exampleValue: value,
        selector: selector ? `${selector}.${key}` : key,
      })
    }

    if (!inferredField) return

    // Replace unsupported values
    inferredFields[createKey(fieldName)] = inferredField
  })
Example #6
0
 Array.from(store.getState().pages.values()).forEach(page => {
   if (
     !_.includes(statefulPlugins, page.pluginCreatorId) &&
     page.updatedAt < timestamp
   ) {
     deleteComponentsDependencies([page.path])
     deletePage(page)
   }
 })
 var codeSplits = highlightedCode.split(`\n`).map(function (split, i) {
   if (_.includes(lineNumbersHighlight, i + 1)) {
     return {
       highlighted: true,
       code: `<span class="gatsby-highlight-code-line">${split}\n</span>`
     };
   } else {
     return { code: split };
   }
 });
Example #8
0
 const codeSplits = highlightedCode.split(`\n`).map((split, i) => {
   if (_.includes(lineNumbersHighlight, i + 1)) {
     return {
       highlighted: true,
       code: `<span class="gatsby-highlight-code-line">${split}\n</span>`,
     }
   } else {
     return { code: split }
   }
 })
Example #9
0
      const blogPosts = _.filter(result.data.allMarkdownRemark.edges, edge => {
        const slug = _.get(edge, `node.fields.slug`)
        const draft = _.get(edge, `node.frontmatter.draft`)
        if (!slug) return undefined

        if (_.includes(slug, `/blog/`) && !draft) {
          return edge
        }

        return undefined
      })
Example #10
0
const useGlobalGatsby = function() {
  // Never use global install *except* for new and help commands
  if (!_.includes([`new`, `--help`], process.argv[2])) {
    report.panic(
      `A local install of Gatsby was not found. \n` +
        `You should save Gatsby as a site dependency e.g. npm install --save gatsby`
    )
  }

  require(`./bin/cli`)
}
Example #11
0
 visit(ast, `image`, node => {
   if (
     node.title === `Permalink to this headline` ||
     node.title === 'Permalink to this definition'
   ) {
     toRemove.push(node);
   } else {
     if (_.includes(node.title, `Permalink`)) {
       console.log(node);
     }
   }
 });
      const readingPosts = _.filter(allMarkdownPages, edge => {
        const slug = _.get(edge, `node.fields.slug`)
        if (!slug) {
          return false
        }

        if (_.includes(slug, `/reading/`)) {
          return true
        }

        return false
      })
Example #13
0
 visited.set(entrypoint, requires.reduce((acc, dep) => {
   debug(`found ${dep}`);
   if (dep.startsWith(`.`)) {
     debug(`${dep} is relative, descending`);
     const next = walk(path.resolve(path.dirname(entrypoint), dep));
     acc = new Set([...acc, ...next]);
   }
   else if (!builtins.includes(dep)) {
     debug(`found dependency ${dep}`);
     acc.add(requireToPackage(dep));
   }
   return acc;
 }, new Set()));
Example #14
0
      result.data.allMarkdownRemark.edges.forEach(edge => {
        const slug = _.get(edge, `node.fields.slug`)
        if (!slug) return

        if (!_.includes(slug, `/blog/`)) {
          createPage({
            path: `${edge.node.fields.slug}`, // required
            component: slash(
              edge.node.fields.package ? localPackageTemplate : docsTemplate
            ),
            context: {
              slug: edge.node.fields.slug,
            },
          })
        }
      })
  _.each(exampleValue, (value, key) => {
    // Remove fields for traversing through nodes as we want to control
    // setting traversing up not try to automatically infer them.
    if (isRoot && EXCLUDE_KEYS[key]) return

    // Input arguments on linked fields aren't currently supported
    if (_.includes(key, `___NODE`)) return

    let field = inferGraphQLInputFields({
      nodes,
      value,
      prefix: `${prefix}${_.upperFirst(key)}`,
    })

    if (field == null) return
    inferredFields[createKey(key)] = field
  })
Example #16
0
const depsToVersions = _.curry((rootPkg, deps) => deps.reduce((acc, dep) => {
  if (builtins.includes(dep)) {
    return acc;
  }

  acc[dep] = rootPkg.dependencies[dep] || rootPkg.devDependencies[dep] || rootPkg.optionalDependencies[dep];

  if (!acc[dep]) {
    try {
      acc[dep] = require(`./packages/node_modules/${dep}/package.json`).version;
    }
    catch (err) {
      // eslint-disable-next-line no-console
      console.warn(err);
      throw new Error(`Failed to determine version for ${dep}, Is it missing from package.json?`);
    }
  }
  return acc;
}, {}));
Example #17
0
 allPackages.forEach(edge => {
   if (_.includes(localPackagesArr, edge.node.title)) {
     createPage({
       path: edge.node.slug,
       component: slash(localPackageTemplate),
       context: {
         slug: edge.node.slug,
         id: edge.node.id,
         layout: `plugins`,
       },
     })
   } else {
     createPage({
       path: edge.node.slug,
       component: slash(remotePackageTemplate),
       context: {
         slug: edge.node.slug,
         id: edge.node.id,
         layout: `plugins`,
       },
     })
   }
 })
Example #18
0
async function onCreateNode({ node, getNode, actions, loadNodeContent }) {
  const { createNode, createParentChildLink } = actions
  const fileExtsToProcess = [`js`, `jsx`, `ts`, `tsx`]

  // This only processes javascript and typescript files.
  if (!_.includes(fileExtsToProcess, node.extension)) {
    return
  }

  const code = await loadNodeContent(node)
  const options = {
    sourceType: `module`,
    allowImportExportEverywhere: true,
    plugins: [
      `jsx`,
      `doExpressions`,
      `objectRestSpread`,
      [
        `decorators`,
        {
          decoratorsBeforeExport: true,
        },
      ],
      `classProperties`,
      `exportExtensions`,
      `asyncGenerators`,
      `functionBind`,
      `functionSent`,
      `dynamicImport`,
      `flow`,
    ],
  }

  let exportsData, frontmatter, error
  try {
    const ast = babylon.parse(code, options)

    const parseData = function parseData(node) {
      let value

      if (node.type === `TemplateLiteral`) {
        // Experimental basic support for template literals:
        // Extract and join any text content; ignore interpolations
        value = node.quasis.map(quasi => quasi.value.cooked).join(``)
      } else if (node.type === `ObjectExpression`) {
        value = {}
        node.properties.forEach(elem => {
          value[elem.key.name] = parseData(elem.value)
        })
      } else if (node.type === `ArrayExpression`) {
        value = node.elements.map(elem => parseData(elem))
      } else {
        value = node.value
      }

      return value
    }

    frontmatter = {}
    error = false
    traverse(ast, {
      AssignmentExpression: function AssignmentExpression(astPath) {
        if (
          astPath.node.left.type === `MemberExpression` &&
          astPath.node.left.property.name === `frontmatter`
        ) {
          astPath.node.right.properties.forEach(node => {
            frontmatter[node.key.name] = parseData(node.value)
          })
        }
      },
      ExportNamedDeclaration: function ExportNamedDeclaration(astPath) {
        const { declaration } = astPath.node
        if (declaration && declaration.type === `VariableDeclaration`) {
          const dataVariableDeclarator = _.find(
            declaration.declarations,
            d => d.id.name === `frontmatter`
          )

          if (dataVariableDeclarator && dataVariableDeclarator.init) {
            dataVariableDeclarator.init.properties.forEach(node => {
              frontmatter[node.key.name] = parseData(node.value)
            })
          }
        }
      },
    })
  } catch (e) {
    // stick the error on the query so the user can
    // react to an error as they see fit
    error = {
      err: true,
      code: e.code,
      message: e.message,
      stack: e.stack,
    }
  } finally {
    // only create node if frontmatter is not empty
    if (!_.isEmpty(frontmatter)) {
      exportsData = {
        ...frontmatter,
        error: error,
      }

      const objStr = JSON.stringify(node)
      const contentDigest = crypto
        .createHash(`md5`)
        .update(objStr)
        .digest(`hex`)

      const nodeData = {
        id: `${node.id} >>> JavascriptFrontmatter`,
        children: [],
        parent: node.id,
        node: { ...node },
        internal: {
          contentDigest,
          type: `JavascriptFrontmatter`,
        },
      }

      nodeData.frontmatter = { ...exportsData }

      if (node.internal.type === `File`) {
        nodeData.fileAbsolutePath = node.absolutePath
      }

      createNode(nodeData)
      createParentChildLink({ parent: node, child: nodeData })
    }
  }
}
Example #19
0
actions.createNodeField = (
  { node, name, value, fieldName, fieldValue }: CreateNodeInput,
  plugin: Plugin,
  actionOptions?: ActionOptions
) => {
  if (fieldName) {
    console.warn(
      `Calling "createNodeField" with "fieldName" is deprecated. Use "name" instead`
    )
    if (!name) {
      name = fieldName
    }
  }
  if (fieldValue) {
    console.warn(
      `Calling "createNodeField" with "fieldValue" is deprecated. Use "value" instead`
    )
    if (!value) {
      value = fieldValue
    }
  }
  // Ensure required fields are set.
  if (!node.internal.fieldOwners) {
    node.internal.fieldOwners = {}
  }
  if (!node.fields) {
    node.fields = {}
  }

  /**
   * Normalized name of the field that will be used in schema
   */
  const schemaFieldName = _.includes(name, `___NODE`)
    ? name.split(`___`)[0]
    : name

  // Check that this field isn't owned by another plugin.
  const fieldOwner = node.internal.fieldOwners[schemaFieldName]
  if (fieldOwner && fieldOwner !== plugin.name) {
    throw new Error(
      stripIndent`
      A plugin tried to update a node field that it doesn't own:

      Node id: ${node.id}
      Plugin: ${plugin.name}
      name: ${name}
      value: ${value}
      `
    )
  }

  // Update node
  node.fields[name] = value
  node.internal.fieldOwners[schemaFieldName] = plugin.name

  return {
    type: `ADD_FIELD_TO_NODE`,
    plugin,
    ...actionOptions,
    payload: node,
  }
}
 groups[val] = _.filter(connectionNodes, n =>
   _.includes(_.get(n, fieldName), val)
Example #21
0
const filterOptionals = (key) => OPTIONAL.includes(key)
Example #22
0
  _.each(resolvedExample, (value, key) => {
    // Remove fields common to the top-level of all nodes.  We add these
    // elsewhere so don't need to infer their type.
    if (value === INVALID_VALUE || (isRoot && EXCLUDE_KEYS[key])) return

    // Several checks to see if a field is pointing to custom type
    // before we try automatic inference.
    const nextSelector = selector ? `${selector}.${key}` : key
    const fieldSelector = `${rootTypeName}.${nextSelector}`

    let fieldName = key
    let inferredField

    // First check for manual field => type mappings in the site's
    // gatsby-config.js
    if (mapping && _.includes(Object.keys(mapping), fieldSelector)) {
      inferredField = inferFromMapping(value, mapping, fieldSelector, types)

      // Second if the field has a suffix of ___node. We use then the value
      // (a node id) to find the node and use that node's type as the field
    } else if (key.includes(`___NODE`)) {
      ;[fieldName] = key.split(`___`)
      inferredField = inferFromFieldName(value, nextSelector, types)
      lazyFields.add(typeName, fieldName)
    }

    // Replace unsupported values
    const sanitizedFieldName = createKey(fieldName)

    // If a pluging has already provided a type for this, don't infer it.
    if (ignoreFields && ignoreFields.includes(sanitizedFieldName)) {
      return
    }

    // Finally our automatic inference of field value type.
    if (!inferredField) {
      inferredField = inferGraphQLType({
        nodes,
        types,
        exampleValue: value,
        selector: nextSelector,
      })
    }

    if (!inferredField) return

    // If sanitized field name is different from original field name
    // add resolve passthrough to reach value using original field name
    if (sanitizedFieldName !== fieldName) {
      const {
        resolve: fieldResolve,
        ...inferredFieldWithoutResolve
      } = inferredField

      // Using copy if field as we sometimes have predefined frozen
      // field definitions and we can't mutate them.
      inferredField = inferredFieldWithoutResolve

      if (fieldResolve) {
        // If field has resolver, call it with adjusted resolveInfo
        // that points to original field name
        inferredField.resolve = (source, args, context, resolveInfo) =>
          fieldResolve(source, args, context, {
            ...resolveInfo,
            fieldName: fieldName,
          })
      } else {
        inferredField.resolve = source => source[fieldName]
      }
    }

    inferredFields[sanitizedFieldName] = inferredField
  })
Example #23
0
 .pages.filter(p => !_.includes(statefulPlugins, p.pluginCreatorId))
Example #24
0
 .filter(n => _.includes(newOrUpdatedEntries, n.id))
Example #25
0
 .on(`add`, path => {
   if (!_.includes(files, path)) {
     _createPage(path, pagesDirectory, createPage)
     files.push(path)
   }
 })
module.exports = type => {
  const enumValues = buildFieldEnumValues({
    nodes: type.nodes,
    typeName: type.name,
  })
  const { connectionType: groupConnection } = connectionDefinitions({
    name: _.camelCase(`${type.name} groupConnection`),
    nodeType: type.nodeObjectType,
    connectionFields: () => {
      return {
        field: { type: GraphQLString },
        fieldValue: { type: GraphQLString },
        totalCount: { type: GraphQLInt },
      }
    },
  })

  return {
    totalCount: {
      type: GraphQLInt,
    },
    distinct: {
      type: new GraphQLList(GraphQLString),
      args: {
        field: {
          type: new GraphQLEnumType({
            name: _.camelCase(`${type.name} distinct enum`),
            values: enumValues,
          }),
        },
      },
      resolve(connection, args) {
        let fieldName = args.field
        if (_.includes(args.field, `___`)) {
          fieldName = args.field.replace(/___/g, `.`)
        }
        const fields = connection.edges.map(edge => _.get(edge.node, fieldName))
        return _.sortBy(_.filter(_.uniq(_.flatten(fields)), _.identity))
      },
    },
    group: {
      type: new GraphQLList(groupConnection),
      args: {
        ...connectionArgs,
        field: {
          type: new GraphQLEnumType({
            name: _.camelCase(`${type.name} group enum`),
            values: enumValues,
          }),
        },
      },
      resolve(connection, args) {
        const fieldName = args.field.replace(/___/g, `.`)
        const connectionNodes = connection.edges.map(edge => edge.node)

        let groups = {}
        // Do a custom grouping for arrays (w/ a group per array value)
        // Find the first node with this field and check if it's an array.
        if (_.isArray(_.get(_.find(connectionNodes, fieldName), fieldName))) {
          const values = _.uniq(
            _.reduce(
              connectionNodes,
              (vals, n) => {
                if (_.has(n, fieldName)) {
                  return vals.concat(_.get(n, fieldName))
                } else {
                  return vals
                }
              },
              []
            )
          )
          values.forEach(val => {
            groups[val] = _.filter(connectionNodes, n =>
              _.includes(_.get(n, fieldName), val)
            )
          })
        } else {
          groups = _.groupBy(connectionNodes, fieldName)
        }
        const groupConnections = []

        // Do default sort by fieldValue
        const sortedFieldValues = _.sortBy(_.keys(groups))
        _.each(sortedFieldValues, fieldValue => {
          const groupNodes = groups[fieldValue]
          const groupConn = connectionFromArray(groupNodes, args)
          groupConn.totalCount = groupNodes.length
          groupConn.field = fieldName
          groupConn.fieldValue = fieldValue
          groupConnections.push(groupConn)
        })

        return groupConnections
      },
    },
  }
}
Example #27
0
 .filter(u => _.includes(usersToAlert, u.id))
Example #28
0
const isImage = image =>
  _.includes(
    [`image/jpeg`, `image/jpg`, `image/png`, `image/webp`, `image/gif`],
    _.get(image, `file.contentType`)
  )
Example #29
0
exports.onCreateNode = ({ node, actions, getNode, reporter }) => {
  const { createNodeField } = actions
  let slug
  if (node.internal.type === `File`) {
    const parsedFilePath = parseFilepath(node.relativePath)
    if (node.sourceInstanceName === `docs`) {
      if (parsedFilePath.name !== `index` && parsedFilePath.dir !== ``) {
        slug = `/${parsedFilePath.dir}/${parsedFilePath.name}/`
      } else if (parsedFilePath.dir === ``) {
        slug = `/${parsedFilePath.name}/`
      } else {
        slug = `/${parsedFilePath.dir}/`
      }
    }
    if (slug) {
      createNodeField({ node, name: `slug`, value: slug })
    }
  } else if (
    node.internal.type === `MarkdownRemark` &&
    getNode(node.parent).internal.type === `File`
  ) {
    const fileNode = getNode(node.parent)
    const parsedFilePath = parseFilepath(fileNode.relativePath)
    // Add slugs for docs pages
    if (fileNode.sourceInstanceName === `docs`) {
      if (parsedFilePath.name !== `index` && parsedFilePath.dir !== ``) {
        slug = `/${parsedFilePath.dir}/${parsedFilePath.name}/`
      } else if (parsedFilePath.dir === ``) {
        slug = `/${parsedFilePath.name}/`
      } else {
        slug = `/${parsedFilePath.dir}/`
      }

      // Set released status and `published at` for blog posts.
      if (_.includes(parsedFilePath.dir, `blog`)) {
        let released = false
        const date = _.get(node, `frontmatter.date`)
        if (date) {
          released = moment.utc().isSameOrAfter(moment.utc(date))
        }
        createNodeField({ node, name: `released`, value: released })

        const canonicalLink = _.get(node, `frontmatter.canonicalLink`)
        const publishedAt = _.get(node, `frontmatter.publishedAt`)

        createNodeField({
          node,
          name: `publishedAt`,
          value: canonicalLink
            ? publishedAt || url.parse(canonicalLink).hostname
            : null,
        })
      }
    }
    // Add slugs for package READMEs.
    if (
      fileNode.sourceInstanceName === `packages` &&
      parsedFilePath.name === `README`
    ) {
      slug = `/packages/${parsedFilePath.dir}/`
      createNodeField({
        node,
        name: `title`,
        value: parsedFilePath.dir,
      })
      createNodeField({ node, name: `package`, value: true })
    }
    if (slug) {
      createNodeField({ node, name: `anchor`, value: slugToAnchor(slug) })
      createNodeField({ node, name: `slug`, value: slug })
    }
  } else if (node.internal.type === `AuthorYaml`) {
    slug = `/contributors/${slugify(node.id, {
      lower: true,
    })}/`
    createNodeField({ node, name: `slug`, value: slug })
  } else if (node.internal.type === `SitesYaml` && node.main_url) {
    const parsed = url.parse(node.main_url)
    const cleaned = parsed.hostname + parsed.pathname
    slug = `/showcase/${slugify(cleaned)}`
    createNodeField({ node, name: `slug`, value: slug })

    // determine if screenshot is available
    const screenshotNode = node.children
      .map(childID => getNode(childID))
      .find(node => node.internal.type === `Screenshot`)

    createNodeField({ node, name: `hasScreenshot`, value: !!screenshotNode })
  } else if (node.internal.type === `StartersYaml` && node.repo) {
    // To develop on the starter showcase, you'll need a GitHub
    // personal access token. Check the `www` README for details.
    // Default fields are to avoid graphql errors.
    const { owner, name: repoStub } = parseGHUrl(node.repo)
    const defaultFields = {
      slug: `/${owner}/${repoStub}/`,
      stub: repoStub,
      name: ``,
      description: ``,
      stars: 0,
      lastUpdated: ``,
      owner: ``,
      githubFullName: ``,
      gatsbyMajorVersion: [[`no data`, `0`]],
      allDependencies: [[`no data`, `0`]],
      gatsbyDependencies: [[`no data`, `0`]],
      miscDependencies: [[`no data`, `0`]],
    }

    if (!process.env.GITHUB_API_TOKEN) {
      return createNodeField({
        node,
        name: `starterShowcase`,
        value: {
          ...defaultFields,
        },
      })
    } else {
      return Promise.all([
        getpkgjson(node.repo),
        githubApiClient.request(`
            query {
              repository(owner:"${owner}", name:"${repoStub}") {
                name
                stargazers {
                  totalCount
                }
                createdAt
                pushedAt
                owner {
                  login
                }
                nameWithOwner
              }
            }
          `),
      ])
        .then(results => {
          const [pkgjson, githubData] = results
          const {
            stargazers: { totalCount: stars },
            pushedAt: lastUpdated,
            owner: { login: owner },
            name,
            nameWithOwner: githubFullName,
          } = githubData.repository

          const { dependencies = [], devDependencies = [] } = pkgjson
          const allDependencies = Object.entries(dependencies).concat(
            Object.entries(devDependencies)
          )

          const gatsbyMajorVersion = allDependencies
            .filter(([key, _]) => key === `gatsby`)
            .map(version => {
              let [gatsby, versionNum] = version
              if (versionNum === `latest` || versionNum === `next`) {
                return [gatsby, `2`]
              }
              return [gatsby, versionNum.replace(/\D/g, ``).charAt(0)]
            })

          // If a new field is added here, make sure a corresponding
          // change is made to "defaultFields" to not break DX
          const starterShowcaseFields = {
            slug: `/${owner}/${repoStub}/`,
            stub: repoStub,
            name,
            description: pkgjson.description,
            stars,
            lastUpdated,
            owner,
            githubFullName,
            gatsbyMajorVersion,
            allDependencies,
            gatsbyDependencies: allDependencies
              .filter(
                ([key, _]) => ![`gatsby-cli`, `gatsby-link`].includes(key) // remove stuff everyone has
              )
              .filter(([key, _]) => key.includes(`gatsby`)),
            miscDependencies: allDependencies.filter(
              ([key, _]) => !key.includes(`gatsby`)
            ),
          }
          createNodeField({
            node,
            name: `starterShowcase`,
            value: starterShowcaseFields,
          })
        })
        .catch(err => {
          reporter.panicOnBuild(
            `Error getting repo data for starter "${repoStub}":\n
            ${err.message}`
          )
        })
    }
  } else if (node.internal.type === `CreatorsYaml`) {
    // Creator pages
    const validTypes = {
      individual: `people`,
      agency: `agencies`,
      company: `companies`,
    }

    if (!validTypes[node.type]) {
      throw new Error(
        `Creators must have a type of “individual”, “agency”, or “company”, but invalid type “${
          node.type
        }” was provided for ${node.name}.`
      )
    }
    slug = `/creators/${validTypes[node.type]}/${slugify(node.name, {
      lower: true,
    })}`
    createNodeField({ node, name: `slug`, value: slug })
  }
  // end Creator pages
  return null
}
 chokidar.watch(pagesGlob).on(`add`, function (path) {
   if (!_.includes(files, path)) {
     _createPage(path, pagesDirectory, createPage);
     files.push(path);
   }
 }).on(`unlink`, function (path) {