trufflesuite/truffle

View on GitHub
packages/compile-common/src/sources.ts

Summary

Maintainability
A
1 hr
Test Coverage
import * as path from "path";

//source content by path
export interface Sources {
  [sourcePath: string]: string;
}

//original paths by transformed path
export interface PathMapping {
  [sourcePath: string]: string;
}

export interface CollectedSources {
  sources: Sources;
  targets: string[];
  originalSourcePaths: PathMapping;
}

/**
 * Collects sources, targets into collections with OS-independent paths,
 * along with a reverse mapping to the original path (for post-processing)
 *
 * @param originalSources - { [originalSourcePath]: contents }
 * @param originalTargets - originalSourcePath[]
 * @param baseDirectory - a directory to remove as a prefix
 * @param replacement - what to replace it with
 * @return { sources, targets, originalSourcePaths }
 */
export function collectSources(
  originalSources: Sources,
  originalTargets: string[] = [],
  baseDirectory: string = "",
  replacement: string = "/"
): CollectedSources {
  const mappedResults = Object.entries(originalSources)
    .filter(
      ([originalSourcePath, _]) =>
        !path.isAbsolute(originalSourcePath) ||
        originalSourcePath.startsWith(baseDirectory)
    )
    .map(([originalSourcePath, contents]) => ({
      originalSourcePath,
      contents,
      sourcePath: getPortableSourcePath(
        replaceRootDirectory(originalSourcePath, baseDirectory, replacement)
      )
    }))
    .map(({ originalSourcePath, sourcePath, contents }) => ({
      sources: {
        [sourcePath]: contents
      },

      // include transformed form as target if original is a target
      targets: originalTargets.includes(originalSourcePath) ? [sourcePath] : [],

      originalSourcePaths: {
        [sourcePath]: originalSourcePath
      }
    }));

  const defaultAccumulator = {
    sources: {},
    targets: [] as string[],
    originalSourcePaths: {}
  };

  return mappedResults.reduce(
    (accumulator, result) => ({
      sources: Object.assign({}, accumulator.sources, result.sources),
      targets: [...accumulator.targets, ...result.targets],
      originalSourcePaths: Object.assign(
        {},
        accumulator.originalSourcePaths,
        result.originalSourcePaths
      )
    }),
    defaultAccumulator
  );
}

/**
 * @param sourcePath - string
 * @return string - operating system independent path
 * @private
 */
function getPortableSourcePath(sourcePath: string): string {
  let replacement = sourcePath;
  //on Windows, replace backslashes with forward slashes
  if (path.sep === "\\") {
    replacement = sourcePath.replace(/\\/g, "/");
  }

  // Turn G:/.../ into /G/.../ for Windows
  if (replacement.length >= 2 && replacement[1] === ":") {
    replacement = "/" + replacement;
    replacement = replacement.replace(":", "");
  }

  return replacement;
}

function replaceRootDirectory(
  sourcePath: string,
  rootDirectory: string,
  replacement: string
): string {
  //make sure root directory ends in a separator
  if (!rootDirectory.endsWith(path.sep)) {
    rootDirectory = rootDirectory + path.sep;
  }
  return sourcePath.startsWith(rootDirectory)
    ? replacement + sourcePath.slice(rootDirectory.length) //remove prefix
    : sourcePath;
}