Repository: gatsbyjs/gatsby
File: packages/gatsby-plugin-page-creator/src/is-valid-collection-path-implementation.ts (TypeScript)

Code before:
import sysPath from "path"
import { Reporter } from "gatsby"
import { CODES, prefixId } from "./error-utils"
import { matchAllPolyfill } from "./path-utils"

// This file is a helper for consumers. It's going to log an error to them if they
// in any way have an incorrect filepath setup for us to predictably use collection
// querying.
//
// Without this, users will can get mystic errors.
export function isValidCollectionPathImplementation(
  filePath: string,
  reporter: Reporter
): boolean {
  const parts = filePath.split(sysPath.sep)
  let passing = false
  let errors = 0

  parts.forEach(part => {
    if (!part.includes(`{`) && !part.includes(`}`)) return

    const model = matchAllPolyfill(/\{([a-zA-Z_]\w*)./g, part) // Search for word before first dot, e.g. Model
    const field = matchAllPolyfill(/{.*?((?<=\w\.)[^}]*)}/g, part) // Search for everything after the first dot, e.g. foo__bar (or in invalid case: foo.bar)

    const models = Array.from(model, m => m[1])
    const fields = Array.from(field, f => f[1])

    try {
      if (
        models.length === 0 ||
        fields.length === 0 ||
        models.length !== fields.length
      ) {
        throw new Error(errorMessage(part))
      }
      for (const m of models) {
        assert(m, /^[a-zA-Z_]\w*$/, errorMessage(part)) // Check that Model is https://spec.graphql.org/draft/#sec-Names
      }
      for (const f of fields) {
        assert(f, /^[a-zA-Z_][\w_()]*$/, errorMessage(part)) // Check that field is foo__bar__baz (and not foo.bar.baz) + https://spec.graphql.org/draft/#sec-Names
      }
    } catch (e) {
      reporter.panicOnBuild({
        id: prefixId(CODES.CollectionPath),
        context: {
          sourceMessage: e.message,
        },
        filePath: filePath,

Review comment (suggestion):
Should the search of the second star be greedy or non-greedy?

Consider these two regexes
```js
/{.*?((?<=\w\.)[^}]*)}/g
/{.*?((?<=\w\.)[^}]*?)}/g
```

Code after:
import sysPath from "path"
import { Reporter } from "gatsby"
import { CODES, prefixId } from "./error-utils"
import { matchAllPolyfill } from "./path-utils"

// This file is a helper for consumers. It's going to log an error to them if they
// in any way have an incorrect filepath setup for us to predictably use collection
// querying.
//
// Without this, users will can get mystic errors.
export function isValidCollectionPathImplementation(
  filePath: string,
  reporter: Reporter
): boolean {
  const parts = filePath.split(sysPath.sep)
  let passing = false
  let errors = 0

  parts.forEach(part => {
    if (!part.includes(`{`) && !part.includes(`}`)) return

    const model = matchAllPolyfill(/\{([a-zA-Z_]\w*)./g, part) // Search for word before first dot, e.g. Model
    const field = matchAllPolyfill(/.*?((?<=\w\.)[^}]*)}/g, part) // Search for everything after the first dot, e.g. foo__bar (or in invalid case: foo.bar)

    try {
      if (
        model.length === 0 ||
        field.length === 0 ||
        model.length !== field.length
      ) {
        throw new Error(errorMessage(part))
      }

      const models = Array.from(model, m => m[1])
      const fields = Array.from(field, f => f[1])

      for (const m of models) {
        assert(m, /^[a-zA-Z_]\w*$/, errorMessage(part)) // Check that Model is https://spec.graphql.org/draft/#sec-Names
      }
      for (const f of fields) {
        assert(f, /^[a-zA-Z_][\w_()]*$/, errorMessage(part)) // Check that field is foo__bar__baz (and not foo.bar.baz) + https://spec.graphql.org/draft/#sec-Names
      }
    } catch (e) {
      reporter.panicOnBuild({
        id: prefixId(CODES.CollectionPath),
        context: {
          sourceMessage: e.message,
        },