Converting Extension from JS to JSX
Tags: vite, react, webpack, jsx
Published: 2024-05-25 06:44:21

This article is part of the [Converting Large Codebase Project to Vite series](https://dev.to/elpddev/converting-large-codebase-project-to-vite-5b20).

All files that contains JSX code and had ".js" extension failed in the browser. 

This happened because the [react swc vite plugin](https://github.com/vitejs/vite-plugin-react-swc) does not compile ".js" files. Instead it send them to the browser as is.

https://github.com/vitejs/vite-plugin-react-swc/blob/b829b03f6476bed60ff5535fe883abc1b0b4e095/src/index.ts#L231

```tsx
const transformWithOptions = async (
  id: string,
  code: string,
  target: JscTarget,
  options: Options,
  reactConfig: ReactConfig,
) => {
  const decorators = options?.tsDecorators ?? false;
  const parser: ParserConfig | undefined = id.endsWith(".tsx")
    ? { syntax: "typescript", tsx: true, decorators }
    : id.endsWith(".ts")
    ? { syntax: "typescript", tsx: false, decorators }
    : id.endsWith(".jsx")
    ? { syntax: "ecmascript", jsx: true }
    : id.endsWith(".mdx")
    ? // JSX is required to trigger fast refresh transformations, even if MDX already transforms it
      { syntax: "ecmascript", jsx: true }
    : undefined;
  if (!parser) return;

  let result: Output;
  try {
    result = await transform(code, {
      filename: id,
      swcrc: false,
      configFile: false,
      sourceMaps: true,
      jsc: {
        target,
        parser,
        experimental: { plugins: options.plugins },
        transform: {
          useDefineForClassFields: true,
          react: reactConfig,
        },
      },
    });
```

That is an [explicit decision](https://github.com/vitejs/vite-plugin-react-swc/issues/57#issuecomment-1425931254) made by the plugin authors. 

## Chosen Solution

So in order to have the plugin take all those files and transpile them with SWC, we had to change the extension all of "js/ts" files that contains JSX to "jsx/tsx" equivalent extension.

Without a transpiler in the process, the jsx left as is:
https://github.com/vitejs/vite/discussions/3448

The conversion process can be done in several ways. One of them is using a script to go though  all project "js/ts" files, detect if they have any JSX syntax and change their extension.

An example of [such script](https://github.com/vitejs/vite/discussions/3448#discussioncomment-5240383) taken from comment in one of the issues:

```shell
find src -type f | grep "\.[jt]s$" | xargs -n1 grep -HE "(<\/)|(\/>)" | cut -d: -f1 | uniq | awk '{print "mv "$1" "$1"x"}' | sh
```

### Breakdown of Script

#### list All Files Nested in a Target Folder

`find src -type f`

[find](https://www.man7.org/linux/man-pages/man1/find.1.html) - This command searches for all files (-type f) within the src directory and its subdirectories.

The output is a list of the filenames, include their paths.

```shell
./docs/.eslintrc.cjs
./docs/tsconfig.json
./docs/.storybook/main.js
./docs/package.json
./docs/stories/button.stories.ts
```

#### Filter Out Only Needed Files By Extension

`| grep "\.[jt]s$"`

The pipe | takes the output of the find command and passes it to grep.

[grep](https://www.man7.org/linux/man-pages/man1/grep.1.html) - grep "\.[jt]s$" filters the files, keeping only those whose names that end with .js or .ts (JavaScript or TypeScript files). for the grep patterns basic regex can be used as default.

the output is the same format of file paths and names, but filtered only to the files end with js/ts extensions.

```shell
./docs/.storybook/main.js
./docs/stories/button.stories.ts
```

#### Filter Out Files by Existence of Jsx Content Inside Them

`| xargs -n1 grep -HE "(<\/)|(\/>)"`
        
[xargs](https://www.man7.org/linux/man-pages/man1/xargs.1.html) `-n1` takes each file name from the previous output one by one and executes the following grep command.
        
`grep -HE "(<\/)|(\/>)"` 

searches for the pattern </ or /> in each file.
`-H` includes the filename of the file grep searched in, in the output, 
`-E` allows for extended regular expressions syntax.

```shell
./docs/stories/button.stories.ts:    </Button>
```

#### Split the Output and Keep Only the File Names

`| cut -d: -f1`

[cut](https://www.man7.org/linux/man-pages/man1/cut.1.html) - remove sections from each line of files

`-d:` splits the output by the `:` delimiter. The previous `grep` output it to separate the file and the content found.
`-f1` keeps only the first field, which is the filename.
 
This removes the line number and matched text from the grep output, leaving only the unique filenames.

```shell
./docs/stories/button.stories.ts
```

#### Reduce the File List to Unique File Names

`| uniq`
       
[uniq](https://www.man7.org/linux/man-pages/man1/uniq.1.html) filters out any duplicate filenames, so each filename appears only once.

```shell
./docs/stories/button.stories.ts
```

#### Construct a `Rename` Command for the Specific File Name

`| awk '{print "mv "$1" "$1"x"}'`
        
[awk](https://www.man7.org/linux/man-pages/man1/awk.1p.html) constructs a mv (move) command for each file. 

It renames each file by appending an "x" to its name.

```shell
mv ./docs/stories/button.stories.ts ./docs/stories/button.stories.tsx
```
#### Execute the `Rename` Command

`| sh``
        
The final `| sh` executes the constructed `mv` commands in the shell, renaming the files.

## Process

On a large project, you will want to do this conversion in several PRs and not one big chunk, so not to create large conflicts with other people ongoing work.