Repository: gatsbyjs/gatsby
File: packages/gatsby/src/utils/create-node-id.ts (TypeScript)

Code before:
 *       We do need the low collision rate of SHA-1 so we use a local (global) cache to speed up repetitive calls
 *
 * @param {String | Number} id - A string of arbitrary length
 * @param {String} namespace - Namespace to use for UUID
 *
 * @return {String} - UUID
 */
export function createNodeId(id: string | number, namespace: string): string {
  if (typeof id === `number`) {
    id = id.toString()
  } else if (typeof id !== `string`) {
    report.panic(
      `The \`id\` parameter passed to createNodeId must be a String or Number (got ${typeof id})`
    )
  } else if (typeof namespace !== `string`) {
    report.panic(
      `The \`namespace\` parameter passed to createNodeId must be a String (got ${typeof namespace})`
    )
  }

  // Note: we can use the idCache for namespace as well since the key for ids will include the namespace
  let nsHash = idCache.get(namespace)
  if (!nsHash) {
    nsHash = uuidv5(namespace, seedConstant)
    idCache.set(namespace, nsHash)
  }

  // Calling uuid is relatively expensive because it calls into crypto for sha1.
  // We use a local map to cache calls with the same ns+id pair, which helps a lot.
  const key = `${namespace}_${id}`
  let hash = idCache.get(key)
  if (hash) {
    return hash
  }

  if (
    ![
      `Plugin`,
      `gatsby-source-contentful`,
      `gatsby-transformer-remark`,
    ].includes(namespace)
  ) {
    console.log(`namespace:`, namespace)
  }

  hash = uuidv5(id, nsHash)
  idCache.set(key, hash)
  return hash
}


Review comment (performance):
unlikely, but sharing `idCache` for namespaces and nodes might cause collisions due to how keys are generated:
 - `${namespace}` for namespace UUID
 - `${namespace}_${id}` for node UUID

With example (once again, unlikely) scenario:
Namespace UUID for `gatsby-plugin-foo_bar` and node with `bar` id in `gastsy-plugin-foo` namespace would generate same key for `idCache` - maybe just need to separate tho?

Even then tho, there is still risk of same key for nodes themselves - it just need per namespace lookup maps (instead of concatenating `namespace` and `id`) or maybe just more unlikely joiner string (so not just `_`, but something much more uncommon)? Example for above potential issue: namespace `A` and id `foo_bar` generates same key as namespace `A_foo` and id `bar`

Code after:
 * High level this step is meant to prevent people from using our `id` to have meaning in their site and it's meant
 * to make sure the id ends up being short, whatever the input size was.
 *
 * Note: UUID is relatively slow because it calls into the native crypto library to generate SHA-1 hashes.
 *       We do need the low collision rate of SHA-1 so we use a local (global) cache to speed up repetitive calls
 *
 * @param {String | Number} id - A string of arbitrary length
 * @param {String} namespace - Namespace to use for UUID
 *
 * @return {String} - UUID
 */
export function createNodeId(id: string | number, namespace: string): string {
  if (typeof id === `number`) {
    id = id.toString()
  } else if (typeof id !== `string`) {
    report.panic(
      `The \`id\` parameter passed to createNodeId must be a String or Number (got ${typeof id})`
    )
  } else if (typeof namespace !== `string`) {
    report.panic(
      `The \`namespace\` parameter passed to createNodeId must be a String (got ${typeof namespace})`
    )
  }

  let nsHash = unprefixedCache.get(namespace)
  if (!nsHash) {
    nsHash = uuidv5(namespace, seedConstant) as string
    unprefixedCache.set(namespace, nsHash)
  }

  // Calling uuid is relatively expensive because it calls into crypto for sha1.
  // We use a local map to cache calls with the same ns+id pair, which helps a lot.
  let nsCache = namespacedCache.get(namespace)
  if (!nsCache) {
    nsCache = new Map()
    namespacedCache.set(namespace, nsCache)
  }

  let hash = nsCache.get(id)
  if (hash) {
    return hash
  }

  hash = uuidv5(id, nsHash) as string
  nsCache.set(id, hash)
  return hash
}