Skip to content

Instantly share code, notes, and snippets.

@thedaviddias
Forked from FermiDirak/reactathon2020.js
Created December 7, 2020 19:48
Show Gist options
  • Select an option

  • Save thedaviddias/b8d743eec74825ed76c697e7363a8ee6 to your computer and use it in GitHub Desktop.

Select an option

Save thedaviddias/b8d743eec74825ed76c697e7363a8ee6 to your computer and use it in GitHub Desktop.

Revisions

  1. @FermiDirak FermiDirak revised this gist Nov 30, 2020. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion reactathon2020.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,6 @@ import path from "path";

    const projectDirectory = "my/project/root/directory";


    ///////////////////////////////////////////////////////////////////////////////
    // util functions
    ///////////////////////////////////////////////////////////////////////////////
  2. @FermiDirak FermiDirak created this gist Nov 30, 2020.
    126 changes: 126 additions & 0 deletions reactathon2020.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    import jscodeshift from "jscodeshift";
    import fs from "fs";
    import path from "path";

    const projectDirectory = "my/project/root/directory";


    ///////////////////////////////////////////////////////////////////////////////
    // util functions
    ///////////////////////////////////////////////////////////////////////////////

    /**
    * Walks through a directory tree and creates a list of every component file path
    * note: all React components end in `.jsx` in this codebase
    */
    function walkSync(dir, filelist = []) {
    const files = fs.readdirSync(dir);
    files.forEach((file: any) => {
    if (fs.statSync(`${dir}/${file}`).isDirectory()) {
    filelist = walkSync(`${dir}/${file}`, filelist);
    } else if (path.extname(file) === ".jsx") filelist.push(`${dir}/${file}`);
    });

    return filelist;
    }


    ///////////////////////////////////////////////////////////////////////////////
    // Example 1:
    // Create a list of form components using and not using form UI components
    ///////////////////////////////////////////////////////////////////////////////

    // list of form components in codebase
    const formComponentsFilePaths = walkSync(projectDirectory)
    .filter(filepath => new RegExp(".*Form\.jsx$").test(filepath, "g"));

    const formsUsingFormUI = [];
    const formsNotUsingFormUI = [];

    formComponentsFilePaths.forEach(filepath => {
    try {
    const fileSource = fs.readFileSync(filepath, "utf8");
    const tree = jscodeshift.withParser("javascript")(fileSource);

    // look at the files imports and determine if any form components are imported
    // note: this approach assumes all imports are absolute

    let usesFormUIComponents = false;

    tree
    .find(jscodeshift.ImportDeclaration)
    .find(jscodeshift.Literal)
    .forEach(importPathNode => {
    const importPath = importPathNode.value.value;

    if (formUIComponentImportPaths.has(importPath)) {
    usesFormUIComponents = true;
    }
    });

    if (usesFormUIComponents) {
    formsUsingFormUI.push(filepath);
    } else {
    formsNotUsingFormUI.push(filepath);
    }
    } catch(error) {
    throw new Error(error);
    }
    });

    const stats1 = {
    formsUsingFormUI,
    formsNotUsingFormUI,
    usagePercentage: formsUsingFormUI.length / (formsUsingFormUI.length + formsNotUsingFormUI.length),
    };


    ///////////////////////////////////////////////////////////////////////////////
    // Example 2:
    // Find and list all instances of text literals so teams can go and update
    // them to use i18n
    ///////////////////////////////////////////////////////////////////////////////

    const textLiteralLineNumbersByFile = {
    /* componentPath : list of line numbers, */
    };

    const componentFilePaths = walkSync(projectDirectory);

    componentFilePaths.forEach(filepath => {
    try {
    const fileSource = fs.readFileSync(filepath, "utf8");

    // an AST representation of your source code
    const tree = jscodeshift.withParser("javascript")(fileSource);

    // record the line numbers of any text literals within React nodes.
    // note: this approach only considers text literals passed as children and
    // not text literals passed as props or by other means

    const textLiteralLineNumbers = [];

    tree
    .find(jscodeshift.JSXElement)
    .forEach(jsxElement => {

    let textLiteralChildren = jsxElement.value.children
    .filter(node => node.type === "JSXText")
    .filter(node => !new RegExp("^\\s+$").test(node.value, "g"));

    textLiteralChildren.forEach(node => {
    textLiteralLineNumbers.push(node.loc.start.line);
    });
    });

    textLiteralLineNumbersByFile[filepath] = textLiteralLineNumbers;
    } catch (error) {
    console.error(error);
    }
    });

    const stats2 = {
    filesWithTextLiterals: textLiteralLineNumbersByFile
    .entries()
    .filter(([_filepath, lineNumbers]) => lineNumbers.length !== 0),
    };