async function linkModules()

in internal/linker/link_node_modules.ts [609:712]


  async function linkModules(package_path: string, m: LinkerTreeElement) {
    const symlinkIn = package_path ?
      path.posix.join(bin, package_path, 'node_modules') :
      'node_modules';

    // ensure the parent directory exist
    if (path.dirname(m.name)) {
      await mkdirp(`${symlinkIn}/${path.dirname(m.name)}`);
    }

    if (m.link) {
      const modulePath = m.link;
      let target: string|undefined;
      if (isExecroot) {
        // If we're running out of the execroot, try the execroot path first.
        // If the dependency came in exclusively from a transitive binary target
        // then the module won't be at this path but in the runfiles of the binary.
        // In that case we'll fallback to resolving via runfiles below.
        target = `${startCwd}/${modulePath}`;
      }
      if (!isExecroot || !existsSync(target)) {
        // Transform execroot path to the runfiles manifest path so that
        // it can be resolved with runfiles.resolve()
        let runfilesPath = modulePath;
        if (runfilesPath.startsWith(`${bin}/`)) {
          runfilesPath = runfilesPath.slice(bin.length + 1);
        } else if (runfilesPath === bin) {
          runfilesPath = '';
        }
        const externalPrefix = 'external/';
        if (runfilesPath.startsWith(externalPrefix)) {
          runfilesPath = runfilesPath.slice(externalPrefix.length);
        } else {
          runfilesPath = path.posix.join(workspace, runfilesPath);
        }
        try {
          target = runfiles.resolve(runfilesPath);
          // if we're resolving from a manifest then make sure we don't resolve
          // into the source tree when we are expecting the output tree
          if (runfiles.manifest && modulePath.startsWith(`${bin}/`)) {
            // Check for BAZEL_OUT_REGEX and not /${bin}/ since resolution
            // may be in the `/bazel-out/host` if cfg = "host"
            if (!target.match(_BAZEL_OUT_REGEX)) {
              const e = new Error(`could not resolve module ${runfilesPath} in output tree`);
              (e as any).code = 'MODULE_NOT_FOUND';
              throw e;
            }
          }
        } catch (err) {
          target = undefined;
          log_verbose(`runfiles resolve failed for module '${m.name}': ${err.message}`);
        }
      }
      // Ensure target path absolute for consistency
      if (target && !path.isAbsolute(target)) {
        target = path.resolve(process.cwd(), target);
      }

      const symlinkFile = `${symlinkIn}/${m.name}`;

      // In environments where runfiles are not symlinked (e.g. Windows), existing linked
      // modules are preserved. This could cause issues when a link is created at higher level
      // as a conflicting directory is already on disk. e.g. consider in a previous run, we
      // linked the modules `my-pkg/overlay`. Later on, in another run, we have a module mapping
      // for `my-pkg` itself. The linker cannot create `my-pkg` because the directory `my-pkg`
      // already exists. To ensure that the desired link is generated, we create the new desired
      // link and move all previous nested links from the old module into the new link. Read more
      // about this in the description of `createSymlinkAndPreserveContents`.
      const stats = await gracefulLstat(symlinkFile);
      const isLeftOver =
          (stats !== null && await isLeftoverDirectoryFromLinker(stats, symlinkFile));

      // Check if the target exists before creating the symlink.
      // This is an extra filesystem access on top of the symlink but
      // it is necessary for the time being.
      if (target && await exists(target)) {
        if (stats !== null && isLeftOver) {
          await createSymlinkAndPreserveContents(stats, symlinkFile, target);
        } else {
          await symlinkWithUnlink(target, symlinkFile, stats);
        }
      } else {
        if (!target) {
          log_verbose(`no symlink target found for module ${m.name}`);
        } else {
          // This can happen if a module mapping is propogated from a dependency
          // but the target that generated the mapping in not in the deps. We don't
          // want to create symlinks to non-existant targets as this will
          // break any nested symlinks that may be created under the module name
          // after this.
          log_verbose(`potential target ${target} does not exists for module ${m.name}`);
        }
        if (isLeftOver) {
          // Remove left over directory if it exists
          await unlink(symlinkFile);
        }
      }
    }

    // Process each child branch concurrently
    if (m.children) {
      await Promise.all(m.children.map(m => linkModules(package_path, m)));
    }
  }