Converting Extension from JS to JSX

elpddev

Eyal Lapid

Posted on May 25, 2024

Converting Extension from JS to JSX

This article is part of the Converting Large Codebase Project to Vite series.

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

This happened because the react swc vite plugin 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

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,
        },
      },
    });
Enter fullscreen mode Exit fullscreen mode

That is an explicit decision 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 taken from comment in one of the issues:

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

Breakdown of Script

list All Files Nested in a Target Folder

find src -type f

find - 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.

./docs/.eslintrc.cjs
./docs/tsconfig.json
./docs/.storybook/main.js
./docs/package.json
./docs/stories/button.stories.ts
Enter fullscreen mode Exit fullscreen mode

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 - 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.

./docs/.storybook/main.js
./docs/stories/button.stories.ts
Enter fullscreen mode Exit fullscreen mode

Filter Out Files by Existence of Jsx Content Inside Them

| xargs -n1 grep -HE "(<\/)|(\/>)"

xargs -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.

./docs/stories/button.stories.ts:    </Button>
Enter fullscreen mode Exit fullscreen mode

Split the Output and Keep Only the File Names

| cut -d: -f1

cut - 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.

./docs/stories/button.stories.ts
Enter fullscreen mode Exit fullscreen mode

Reduce the File List to Unique File Names

| uniq

uniq filters out any duplicate filenames, so each filename appears only once.

./docs/stories/button.stories.ts
Enter fullscreen mode Exit fullscreen mode

Construct a Rename Command for the Specific File Name

| awk '{print "mv "$1" "$1"x"}'

awk constructs a mv (move) command for each file.

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

mv ./docs/stories/button.stories.ts ./docs/stories/button.stories.tsx
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
elpddev
Eyal Lapid

Posted on May 25, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related