Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI Version #45

Open
jlarmstrongiv opened this issue Mar 16, 2022 · 4 comments
Open

CLI Version #45

jlarmstrongiv opened this issue Mar 16, 2022 · 4 comments
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed

Comments

@jlarmstrongiv
Copy link

jlarmstrongiv commented Mar 16, 2022

It would be wonderful for the repo-visualizer to have a CLI version, so that it can used locally or in other environments.

Simply put, all the code is already written in https://github.com/githubocto/repo-visualizer/blob/main/src/index.jsx

Just make another version without the actions code, bundle, and publish. It is fairly straightforward with https://www.npmjs.com/package/tsup and https://www.npmjs.com/package/commander

For example:

import fs from "fs-extra";
import React from "react";
import ReactDOMServer from "react-dom/server.js";
import { Tree } from "../vendor/repo-visualizer/src/Tree";
import { processDir } from "../vendor/repo-visualizer/src/process-dir.js";

export const main = async () => {
  const rootPath = ""; // Micro and minimatch do not support paths starting with ./
  const maxDepth = 9;
  const colorEncoding = "type";
  const excludedPathsString =
    "node_modules,bower_components,dist,out,build,eject,.next,.netlify,.yarn,.git,.vscode,package-lock.json,yarn.lock";
  const excludedPaths = excludedPathsString.split(",").map((str) => str.trim());

  // Split on semicolons instead of commas since ',' are allowed in globs, but ';' are not + are not permitted in file/folder names.

  // const excludedGlobsString =
  //   ""
  //   "frontend/*.spec.js;**/*.{png,jpg};**/!(*.module).ts";
  // const excludedGlobs = excludedGlobsString.split(";");
  const excludedGlobs = [
    "**/node_modules/**",
    "**/dist/**",
    "**/out/**",
    "**/build/**",
    "**/.cache/**",
    "**/.next/**",
    "**/.keystone/**",
    "**/.vercel/**",
    "**/package-lock.json",
    "**/*.bundled_*.mjs",
    "**/tmp/**",
    "**/vendor/**",
    "**/.DS_Store/**",
  ];

  const data = await processDir(rootPath, excludedPaths, excludedGlobs);

  const componentCodeString = ReactDOMServer.renderToStaticMarkup(
    // @ts-ignore
    <Tree data={data} maxDepth={+maxDepth} colorEncoding={colorEncoding} />,
  );

  const outputFile = "./repo-visualization.svg";

  await fs.outputFile(outputFile, componentCodeString);
};

and

import { Command } from "commander";
import { readPackageUpSync } from "read-pkg-up";
import { main } from "../core";

const pkg = readPackageUpSync({ cwd: __dirname })!.packageJson;
export const program = new Command()
  .name(pkg.name)
  .description(pkg.description || pkg.name)
  .version(pkg.version, "-v, --version")
  .action((options: { version?: boolean }) => {
    if (options.version) {
      console.log(pkg.version);
    } else {
      main();
    }
  });

You may need to debug the react bundling following tips from egoist/tsup#579 and egoist/tsup#589 and replace lodash with lodash-es

I didn’t link together the options, but commanderjs can support them

Anyway, my rough proof of concept works, but I would much rather see the cli live here in the original, than in a fork.

@jlarmstrongiv
Copy link
Author

For the curious, I am monkey-patching by running:

{
    "run": "dist/cli.js",
    "clean": "rm -rf src/vendor/repo-visualizer",
    "postinstall": "npm run clean && git clone git@github.com:githubocto/repo-visualizer.git ./src/vendor/repo-visualizer && node ./src/vendor/repo-visualizer-postinstall",
}

and

import fg from "fast-glob";
import fs from "fs-extra";
import prependFile from "prepend-file";
import replace from "replace-in-file";

// commit b6ef9a212fc89efed1366f731fc4fe9a2cbc45ef

// always run from pkg.scripts or project root
const files = "src/vendor/repo-visualizer/src/**/*";

async function start() {
  await Promise.all([replaceLodash(), replaceMicromatch(), noLogs(), noPkg()]);
  await tsNoCheck();
}
start().catch(console.error);

async function replaceLodash() {
  await replace({
    files,
    from: /import countBy from "lodash\/countBy";/g,
    to: `import { countBy } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import maxBy from "lodash\/maxBy";/g,
    to: `import { maxBy } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import entries from "lodash\/entries";/g,
    to: `import { entries } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import uniqBy from "lodash\/uniqBy";/g,
    to: `import { uniqBy } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import flatten from "lodash\/flatten";/g,
    to: `import { flatten } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import uniqueId from "lodash\/uniqueId";/g,
    to: `import { uniqueId } from "lodash-es";`,
  });
}

async function replaceMicromatch() {
  await replace({
    files,
    from: /import { isMatch } from "micromatch";/g,
    to: `import micromatch from "micromatch";`,
  });
  await replace({
    files,
    from: /isMatch\(/g,
    to: `micromatch.isMatch(`,
  });
}

async function tsNoCheck() {
  const tsNoCheck = `// @ts-nocheck
`;
  const filePaths = await fg(
    "src/vendor/repo-visualizer/src/**/*.{js,jsx,ts,tsx}",
  );
  await Promise.all(
    filePaths.map((filePath) => prependFile(filePath, tsNoCheck)),
  );
}

async function noLogs() {
  await replace({
    files,
    from: /console\.log\("Looking in ", `\.\/\${path}`\);/g,
    to: ``,
  });
}

async function noPkg() {
  await fs.remove("src/vendor/repo-visualizer/package.json");
  await fs.remove("src/vendor/repo-visualizer/yarn.lock");
}

@jlarmstrongiv
Copy link
Author

jlarmstrongiv commented Mar 16, 2022

Related #41 and #16

@shixin-guo
Copy link
Contributor

shixin-guo commented Mar 18, 2022

good idea, I like it

but,

why do we need to remove the package.json and lock file

async function noPkg() {
  await fs.remove("src/vendor/repo-visualizer/package.json");
  await fs.remove("src/vendor/repo-visualizer/yarn.lock");
}

For the curious, I am monkey-patching by running:

{
    "run": "dist/cli.js",
    "clean": "rm -rf src/vendor/repo-visualizer",
    "postinstall": "npm run clean && git clone git@github.com:githubocto/repo-visualizer.git ./src/vendor/repo-visualizer && node ./src/vendor/repo-visualizer-postinstall",
}

and

import fg from "fast-glob";
import fs from "fs-extra";
import prependFile from "prepend-file";
import replace from "replace-in-file";

// commit b6ef9a212fc89efed1366f731fc4fe9a2cbc45ef

// always run from pkg.scripts or project root
const files = "src/vendor/repo-visualizer/src/**/*";

async function start() {
  await Promise.all([replaceLodash(), replaceMicromatch(), noLogs(), noPkg()]);
  await tsNoCheck();
}
start().catch(console.error);

async function replaceLodash() {
  await replace({
    files,
    from: /import countBy from "lodash\/countBy";/g,
    to: `import { countBy } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import maxBy from "lodash\/maxBy";/g,
    to: `import { maxBy } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import entries from "lodash\/entries";/g,
    to: `import { entries } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import uniqBy from "lodash\/uniqBy";/g,
    to: `import { uniqBy } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import flatten from "lodash\/flatten";/g,
    to: `import { flatten } from "lodash-es";`,
  });
  await replace({
    files,
    from: /import uniqueId from "lodash\/uniqueId";/g,
    to: `import { uniqueId } from "lodash-es";`,
  });
}

async function replaceMicromatch() {
  await replace({
    files,
    from: /import { isMatch } from "micromatch";/g,
    to: `import micromatch from "micromatch";`,
  });
  await replace({
    files,
    from: /isMatch\(/g,
    to: `micromatch.isMatch(`,
  });
}

async function tsNoCheck() {
  const tsNoCheck = `// @ts-nocheck
`;
  const filePaths = await fg(
    "src/vendor/repo-visualizer/src/**/*.{js,jsx,ts,tsx}",
  );
  await Promise.all(
    filePaths.map((filePath) => prependFile(filePath, tsNoCheck)),
  );
}

async function noLogs() {
  await replace({
    files,
    from: /console\.log\("Looking in ", `\.\/\${path}`\);/g,
    to: ``,
  });
}

async function noPkg() {
  await fs.remove("src/vendor/repo-visualizer/package.json");
  await fs.remove("src/vendor/repo-visualizer/yarn.lock");
}

@Wattenberger Wattenberger added enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed labels Mar 25, 2022
@Wattenberger
Copy link
Contributor

great idea, we would definitely welcome a PR!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants