Suggestions for converting workspace path

Heads up to anybody still interested in this topic, Nova 9 changed the contract for its path expansion APIs (i.e. normalize() and expanduser()). Where, before, they would return a path that included the “/Volumes” mount point, they now return bog standard *nix paths with “/” as the root.

If you need a “nixalised” version of your path, starting with Nova 9, path.normalize() is the way. If you still need to get the root mount point, the following modification of my code works on both old and new versions of Nova:

function rootDrive () {
  // Until Nova 9, path expansion functions like “normalize()” and “expanduser()”
  // returned paths including the root volume mount point (by default,
  // “/Volumes/Macintosh HD”). Expanding “/” thus gave us the mount point.
  const expanded = nova.path.normalize(sep)
  if (expanded !== sep) return expanded

  // Since Nova 9, path expansion functions like “normalize()” and “expanduser()”
  // return paths in standard *nix notation, i.e. anchored at “/”. Normalising
  // the mount point gives us “/”, while other volumes are unaffected.
  const root = nova.path.join(sep, 'Volumes')
  for (const name of nova.fs.listdir(root)) {
    const path = nova.path.join(root, name)
    if (nova.path.normalize(path) === sep) return path
  }

  // Our Hail Mary against contract breaches.
  const macDefault = nova.path.join(root, 'Macintosh HD')
  if (nova.fs.stat(macDefault).isSymbolicLink()) return macDefault
  throw new Error(`Unable to locate mount point for root path “${sep}”`)
}

(sep is a module level constant with a value of “/”). Also, if you need to consistently get *nix root type paths on versions of Nova below and above 9, this will work:

function nixalize (path) {
  const normalised = nova.path.normalize(path)
  if (!nova.path.isAbsolute(normalised)) return normalised

  const parts = nova.path.split(normalised)
  if (parts.length < 3 || parts[1] !== 'Volumes') return normalised

  const root = rootDrive()
  const same = nova.path.split(root).every((el, idx) => parts[idx] === el)
  return same ? nova.path.join(sep, ...parts.slice(3)) : normalised
}

Shameless plug: the reason I noticed this is that I am currently writing a kind of meta-extension – a Nova extension for Nova extension development, tentatively called NovaNova. One part of it is a library of utility functions, compatibility components and higher level abstractions for several areas of extension development than the raw Nova API provides, basically pulled out of other extensions I am working on. The functions above are part of that.

The other part is the integration of the QUnit test framework into Nova, and by this, I do not mean the ability to run QUnit tests from Nova, but the ability to test extension code running in the Nova runtime. As a start, I had written some tests for the library, notably one that tested the contract for nova.path.expand('/'). Guess what failed right after updating to Nova 9 …