{"size": 5147, "ext": "js", "max_stars_repo_path": "run.js", "max_stars_repo_name": "pipedrive/CodeshiftCommunity", "max_stars_repo_stars_event_min_datetime": "2021-11-02T15:03:21.000Z", "max_stars_repo_stars_event_max_datetime": "2021-11-02T15:03:21.000Z", "max_issues_repo_path": "run.js", "max_issues_repo_name": "pipedrive/CodeshiftCommunity", "max_issues_repo_issues_event_min_datetime": "2021-11-03T16:25:06.000Z", "max_issues_repo_issues_event_max_datetime": "2022-01-26T21:58:55.000Z", "max_forks_repo_path": "run.js", "max_forks_repo_name": "pipedrive/CodeshiftCommunity", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 1, "max_issues_count": 4, "max_forks_count": null, "avg_line_length": 26.8072916667, "max_line_length": 156, "alphanum_fraction": 0.6314357878}
#!/usr/bin/env node

/* eslint-disable @typescript-eslint/no-var-requires */

/**
 * a temporary utility to run local codemods quickly
 * before we improve stuff upstream.
 */

const helpMsg = `
run.js parser=flow codemodsToRun fileOrDirectoryToModify extensions="js,jsx,ts,tsx"

codemodsToRun - paths to codemods which have a codeshift.config.js file.
                separated by spaces.
                can also just provide keys from the "shorthands.json" file

examples:

./run.js flow ./community/@pipedrive__convention-ui-react/src/5.0.0/transform.ts ~/projects/some-project-which-uses-cui4/src/
./run.js flow cui5                                                               ~/projects/some-project-which-uses-cui4/src/ # cui5 is from shorthands.json

`;

const path = require('path');
const cp = require('child_process');

const peekNextArg = () => process.argv[0];
const eatNextArg = () => process.argv.shift();

const shouldPrintHelp = () => (
  (should =
    !process.argv.length ||
    ['-h', '--help', '-help', 'help'].includes(peekNextArg())),
  should && console.log(helpMsg),
  should
);

const parseArgv = () => (
  process.argv.splice(0, 2),
  shouldPrintHelp() && process.exit(1),
  {
    parser: eatNextArg() || 'flow',
    transformsToRun: parseArrayFromCsv(eatNextArg() || ''),
    fileOrDirectoryToModify: eatNextArg() || '',
    extensions: eatNextArg() || 'js,jsx,ts,tsx',
  }
);

/**
 * will pipe stdio as if we were running the command ourselves!
 *
 * see https://stackoverflow.com/a/47338488/9285308
 *
 * usage:
 *
 * ```js
 * const command = "ls -la";
 * require("child_process").execSync(command, { ...pipeStdioOpts() });
 * ```
 *
 */
const pipeStdioOpts = (cwd = process.cwd()) => ({ cwd, stdio: 'inherit' });
const execSyncP = (cmd, opts) =>
  cp.execSync(cmd, { ...opts, ...pipeStdioOpts() });

const shorthands = require(path.join(__dirname, './shorthands.json'));
console.log({ shorthands });

run();

function run() {
  process.on('SIGTERM', () => process.exit(1));

  if (!!process.env.OLD_FULL_REBUILD) {
    execSyncP('yarn clean');
    execSyncP('yarn install');
    execSyncP('yarn types:check');
  }

  let {
    parser, //
    transformsToRun,
    fileOrDirectoryToModify,
    extensions,
  } = parseArgv();

  transformsToRun = transformsToRun
    .map(t => {
      if (t in shorthands) {
        return resolveTransformsFromShorthand(shorthands, t);
      } else {
        if (!!process.env.OLD_FULL_REBUILD) {
          const dir = path.dirname(t);
          const cmd = `yarn --cwd ${dir} build`;
          console.log('transform to run, build cmd', { cmd });
          execSyncP(cmd);
        }
        return t;
      }
    })
    .flat();
  const transformsToRunStr = transformsToRun.join(',');

  console.log({ transformsToRun });

  const cliPath = path.join(__dirname, './packages/cli/bin/codeshift-cli.js');

  const cmdToExec = `${cliPath} --parser ${parser} -e ${extensions} -t ${transformsToRunStr} ${fileOrDirectoryToModify}`;
  console.log({ cmdToExec });

  execSyncP(cmdToExec);
}

function parseArrayFromCsv(csv = '') {
  return csv
    .split(',')
    .filter(c => !!c)
    .map(c => c.trim())
    .filter(c => !!c);
}

function resolveTransformsFromShorthand(
  shorthands,
  currentShorthandKey,
  alreadyVisitedShorthands = [currentShorthandKey],
) {
  if (!(currentShorthandKey in shorthands)) {
    throw new Error('non-existing shorthand: ' + currentShorthandKey + '\n');
  }

  const [relPathToCodemodPkg, transformVersion] = shorthands[
    currentShorthandKey
  ];

  if (relPathToCodemodPkg === 'alias') {
    const unaliasedShorthands = transformVersion;

    return unaliasedShorthands
      .map(shorthand => {
        if (alreadyVisitedShorthands.includes(shorthand)) {
          console.error('\ncycle in shorthands.json file:', {
            shorthand,
            alreadyVisitedShorthands,
          });

          throw new Error(
            'cycle in shorthands.json file. see above error output.\n',
          );
        }

        return resolveTransformsFromShorthand(
          shorthands,
          shorthand,
          alreadyVisitedShorthands.concat(shorthand),
        );
      })
      .flat();
  }

  const pathToCodemodPkg = path.join(__dirname, relPathToCodemodPkg);

  if (!!process.env.OLD_FULL_REBUILD) {
    execSyncP(`yarn --cwd ${pathToCodemodPkg} build`);
    console.log('built');
  }

  const pathToCodemodConfig = path.join(
    pathToCodemodPkg,
    // 'dist',
    'src',
    'codeshift.config.js',
  );

  const codemodCfg = require(pathToCodemodConfig);
  console.log({ pathToCodemodConfig, codemodCfg, __dirname });

  const { transforms } = codemodCfg;

  const transformsApplicable = Object.entries(transforms)
    .map(([version, relPathToTransform]) => {
      if (version === transformVersion) {
        // return relPathToTransform;
        // return path.join(pathToCodemodPkg, 'dist', relPathToTransform); // TODO must ensure it's compiled / run with ts-node / require from 'dist'

        return path.join(pathToCodemodPkg, 'src', relPathToTransform);
      }
    })
    .filter(x => !!x);

  return transformsApplicable;
}
