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

Added css to css module script #277

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open

Conversation

somya51p
Copy link
Contributor

@somya51p somya51p commented Apr 16, 2022

@jennydaman @PintoGideon I have created the script to convert CSS to CSS modules using regular expressions in js. Please review the PR and suggest changes if any.

@sherrif10
Copy link

@somya51p What exactly does this do. How can i test this generally.

@somya51p
Copy link
Contributor Author

@somya51p What exactly does this do. How can i test this generally.

Hey, @sherrif10 It's associated with issue #167. This script would automatically convert CSS files to CSS modules.

Copy link
Collaborator

@jennydaman jennydaman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disclaimer: I haven't tried running this yet, I will try later.

fs.rename(cssFileName, cssModuleName, () => {});
let data = textData.replace(
`import './${cssFileName.split("/").slice(-1)}'`,
`import styles from './${cssModuleName.split("/").slice(-1)}'`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if multiple stylesheets are imported in the same JSX file? (Or how can you verify that this is not a concern?)

Copy link
Contributor Author

@somya51p somya51p Apr 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if multiple stylesheets are imported in the same JSX file? (Or how can you verify that this is not a concern?)

I analyzed all the files. As in the current codebase, a JSX file has a single CSS file, this script would easily work in our case so it should not be our concern at the very moment. Rest I will check later for the same.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

It would be helpful if you could provide a way to verify this is not a concern, i.e., explain how you did your "analysis."

I looked into this and here's a script I ran:

fd -e jsx -x bash -c '[ $(grep -F .css {} | grep -v node_modules | wc -l) -lt 2 ] || echo "WARNING: {} imports more than 1 local CSS file."' src

This script translates to: "for all JSX files under the src/ directory, count the number of lines which include the substring ".css" but do not include the substring "node_modules", and if this number is greater than or equal to 2, print a warning."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

It would be helpful if you could provide a way to verify this is not a concern, i.e., explain how you did your "analysis."

I looked into this and here's a script I ran:

fd -e jsx -x bash -c '[ $(grep -F .css {} | grep -v node_modules | wc -l) -lt 2 ] || echo "WARNING: {} imports more than 1 local CSS file."' src

This script translates to: "for all JSX files under the src/ directory, count the number of lines which include the substring ".css" but do not include the substring "node_modules", and if this number is greater than or equal to 2, print a warning."

Ok, will take care of that, and also write unit tests. Thanks for the review.

if (blackListedClasses.includes(cls)) {
res += cls;
} else {
res += `\${styles[${ cls }]}`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need quotes around cls?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need quotes around cls?

don't need that for normal className

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During our meeting we talked about how we do need quotes here. I suggest you write examples and a unit test for this case.

@somya51p
Copy link
Contributor Author

somya51p commented May 1, 2022

@jennydaman Please look into the changes made and tests created.

@somya51p
Copy link
Contributor Author

somya51p commented May 15, 2022

Hey @jennydaman, to be precise here are the changes that I had made in the latest commits of this PR.

  1. Created helper functions and added comments.
  2. Added quotes for single className too.
  3. Removed bug for multiple blacklisted classes.
  4. Wrote tests for all the different test cases possible.

Please review it and let me know if I am missing out on something. Thankyou!

Copy link
Collaborator

@jennydaman jennydaman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @somya51p

I am still overwhelmed with a backlog of work but I see that you have worked very hard on this so I am providing some initial feedback now on this weekend. I will add more comments later.


files.forEach((file) => {
if (fs.statSync(`${dirPath }/${ file}`).isDirectory()) {
arrayOfFiles = filesToChange(`${dirPath }/${ file}`, arrayOfFiles);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good use of recursion.

files.forEach((file) => {
if (fs.statSync(`${dirPath }/${ file}`).isDirectory()) {
arrayOfFiles = filesToChange(`${dirPath }/${ file}`, arrayOfFiles);
} else if (file.split(".").slice(-1)[0] === "jsx") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Notice that you have an "if/else if" here, which implies that your function is actually doing more than one thing, and it might be helpful to split it into a function for each thing it's doing.

Here, I would say that the function filesToChange is doing two things:

  1. producing a list of all subpaths under a path
  2. filtering a list of (sub)paths to produce a list of all files which end with .jsx

One option would be to use a library (or a different programming language): https://github.com/isaacs/node-glob

files.forEach((file) => {
if (fs.statSync(`${dirPath }/${ file}`).isDirectory()) {
arrayOfFiles = filesToChange(`${dirPath }/${ file}`, arrayOfFiles);
} else if (file.split(".").slice(-1)[0] === "jsx") {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

try to use the functions which are available to you, such as String.prototype.endsWith

const filesToChange = function (dirPath, fileList) {
const files = fs.readdirSync(dirPath);

let arrayOfFiles = fileList || [];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you make an argument "optional" it's actually a union-type between being "something" or null. Union types can be complicated because you must implement logic to handle both types. For these reasons, it is important that, whenever you have a union type or optional argument, you should provide some documentation indicating it as such.

}
});
return arrayOfFiles;
};
Copy link
Collaborator

@jennydaman jennydaman May 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's how I would've done it:

const fs = require('fs');
const path = require('path');

/**
 * @param {string} startingPath an existing path
 * @returns list of all subpaths under startingPath, including itself
 */
function findAllPaths(startingPath) {
  if (fs.statSync(startingPath).isDirectory()) {
    const subpaths = fs.readdirSync(startingPath).map((subpath) => {
      return findAllPaths(path.join(startingPath, subpath));
    });
    return Array.prototype.concat.apply([startingPath], subpaths);
  }
  return [startingPath];
}

// this helper function here has BOTH a self-explanatory name AND a simple implementation,
// so I am choosing to not add documentation
function filterByFileExtension(paths, suffix) {
  return paths.filter((p) => {
    return fs.statSync(p).isFile() && p.endsWith(suffix);
  });
}

// example

const exampleDirectory = 'ChRIS_store_ui/src';

filterByFileExtension(findAllPaths(exampleDirectory), '.jsx').forEach(p => {
  console.log(p);
});

I've identified the individual units of functionality of this task and created a helper for each one. This way, each function is individually testable, so if either one is doing the wrong thing, it's much easier to identify where the bug is in my code, and also the fix for my code will be much smaller. It can also be easier for others to read, understand, and review my code, because the names of my functions are descriptive and there is docstring where necessary.

@somya51p
Copy link
Contributor Author

somya51p commented May 16, 2022

Hi @somya51p

I am still overwhelmed with a backlog of work but I see that you have worked very hard on this so I am providing some initial feedback now on this weekend. I will add more comments later.

Ohk, Thanks a lot for your time and review :)
I will study your feedback and implement it in the next commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants