plugin-auto-script-jsx

1.0.0-beta.2 • Public • Published

plugin-auto-script-jsx

npm package node compatibility publish node package

概述

当你从别的构建工具 (vue-cli, webpack...) 等切换到 vite,并且你的项目中有使用到 jsx 时,你会发现这样的错误日志:

X [ERROR] The JSX syntax extension is not currently enabled

    script:E:/Desktop/todo/个人项目/xzq-vite-plugins/playground/auto-script-jsx/vue2/src/containJsxVue/test2.vue?id=0:5:6:
      5 │       <div>
        ╵       ^

  The esbuild loader for this file is currently set to "js" but it must be set to "jsx" to be able
  to parse JSX syntax. You can use "loader: { '.js': 'jsx' }" to do that.

这是因为:

Vite 出于编译性能考虑,不会和其他的构建工具一样,对每个处理的 .js 等文件进行完整的 AST 处理,以防万一它包含 JSX

它只会针对有对应的 .jsx 扩展名(如果是 .vue 文件, 只需要 <script lang="jsx"></script>)的文件进行完整的 AST 转换。

这样 Vite 就能避免无谓的编译期间的开销。

因为在大多数情况下,普通的 .js 文件不需要完整的 AST 转换才能在浏览器中工作。

具体原因可以参考:Cannot use jsx syntax in js file.

这时你就需要手动针对每个包含 jsx 的文件手动更改文件后缀名或者添加lang="jsx",当你的文件数量太多时,那可太麻烦了。

此时你可以尝试此插件。

优缺点

优点

  • 自动给包含 jsx代码的 .vue 文件添加 lang="jsx"
  • 兼容 Vue2Vue3

缺点

  • 只支持 Vite
  • 只能给 .vue 文件添加 lang="jsx"

安装

Vite

npm i plugin-auto-script-jsx -D

pnpm add plugin-auto-script-jsx -D

yarn add vue-lazyload -D

vite.config.js

import { defineConfig } from 'vite';
import autoScriptJsxPlugin from 'plugin-auto-script-jsx';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [autoScriptJsxPlugin()],
});

使用案例

以下测试包含了 options apicomposition api 两种风格

相同的 .vue 模板文件

composition api

<script>
export default {
  setup() {
    const dom = <h2 id="contain-jsx-test1">contain-jsx-test1</h2>;
    return () => {
      return <div>{dom}</div>;
    };
  },
};
</script>

options api

<script>
export default {
  render() {
    return (
      <div>
        <h2 id="contain-jsx-test2">contain-jsx-test2</h2>
      </div>
    );
  },
};
</script>

Vue 2(非 2.7)

import { defineConfig } from 'vite';
import { createVuePlugin as vue2 } from 'vite-plugin-vue2';
import autoScriptJsxPlugin from 'plugin-auto-script-jsx';

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
  return {
    plugins: [
      vue2({
        jsx: true,
        jsxOptions: {
          // https://github.com/vuejs/jsx-vue2/tree/dev/packages/babel-preset-jsx#vuebabel-preset-jsx
          // Cannot read properties of undefined (reading '$createElement')
          compositionAPI: true,
        },
      }),
      autoScriptJsxPlugin({
        runTime: false,
      }),
      inspect(),
    ],
    resolve: {
      // 默认不支持 .vue 扩展名
      // 并非 bug, 而是 vite 设计就是如此: https://github.com/vitejs/vite/issues/178#issuecomment-630138450
      // 配置参考: https://vitejs.bootcss.com/config/#resolve-extensions
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
    },
  };
});

runTime: false 的情况下,会在编译时自动给包含 jsx代码的 .vue文件中的 <script> 标签添加 lang = 'jsx'

Vue 2.7

import { defineConfig } from 'vite';
import vue2Dot7 from '@vitejs/plugin-vue2';
import vue2Dot7Jsx from '@vitejs/plugin-vue2-jsx';
import autoScriptJsxPlugin from 'plugin-auto-script-jsx';

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
  return {
    plugins: [
      vue2Dot7(),
      vue2Dot7Jsx(),
      autoScriptJsxPlugin({
        runTime: true,
      }),
    ],
    resolve: {
      // 默认不支持 .vue 扩展名
      // 并非 bug, 而是 vite 设计就是如此: https://github.com/vitejs/vite/issues/178#issuecomment-630138450
      // 配置参考: https://vitejs.bootcss.com/config/#resolve-extensions
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
    },
  };
});

runTime: true 的情况下,会在运行时自动给内存中的包含 jsx代码的 .vue文件中的 <script> 标签添加 lang = 'jsx',注意,此时并不会修改真实的存储在硬盘上的文件。

Vue3

import { defineConfig } from 'vite';
import vue3 from '@vitejs/plugin-vue';
import vue3Jsx from '@vitejs/plugin-vue-jsx';
import autoScriptJsxPlugin from 'plugin-auto-script-jsx';

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
  return {
    plugins: [
      vue3(),
      vue3Jsx(),
      autoScriptJsxPlugin({
        // 不打印日志
        logEnabled: false,
      }),
    ],
    resolve: {
      // 默认不支持 .vue 扩展名
      // 并非 bug, 而是 vite 设计就是如此: https://github.com/vitejs/vite/issues/178#issuecomment-630138450
      // 配置参考: https://vitejs.bootcss.com/config/#resolve-extensions
      extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
    },
  };
});

配置项

以下显示配置的默认值:

autoScriptJsxPlugin({
  // 会打印出当前环境的 Vue 版本, 和对应的文件是否
  // containJsx: 该文件是否包含 jsx; scriptTagContainJsxLang: 该 .vue 文件是否在 script 标签上有 lang = 'jsx'
  logEnabled: true,
  // 默认采用的是在编译时修改文件的策略
  // 如果为 true, 那么就是在运行时对内存中的文件进行修改, 相比于编译时修改, runTime: true, 对用户的真实存储在硬盘中的文件不会有修改
  // 但是它的缺点是: 它需要先关闭依赖预构建, 不然在依赖预构建的扫描依赖的过程中 esbuild 就会因为 loader 配置错误而报错, 根本就走不到插件调用这里来
  runTime: false,
});

log info

> vite

[vite:vue-auto-script-jsx]   {
  VUE_VERSION: '2.7.8',
  isVue2: false,
  isVue2Dot7: true,
  isVue3: false
}
[vite:vue-auto-script-jsx]   parseSFC-Func:  [Function: parse]
[vite:vue-auto-script-jsx]   { containJsx: false, scriptTagContainJsxLang: false }
[vite:vue-auto-script-jsx]   { containJsx: true, scriptTagContainJsxLang: false }
[vite:vue-auto-script-jsx]   target:  E:/Desktop/todo/个人项目/xzq-vite-plugins/playground/auto-script-jsx/vue2.7/src/containJsxVue/test1.vue
[vite:vue-auto-script-jsx]   { containJsx: true, scriptTagContainJsxLang: false }
[vite:vue-auto-script-jsx]   target:  E:/Desktop/todo/个人项目/xzq-vite-plugins/playground/auto-script-jsx/vue2.7/src/containJsxVue/test2.vue
[vite:vue-auto-script-jsx]   { containJsx: false, scriptTagContainJsxLang: false }

警告

runTime: false 时,该插件会对硬盘上存储的真实文件进行修改,因此建议先通过 git 提交一下,保持工作区和暂存区的纯净,然后再运行此插件。

这样方便追踪此插件修改的文件。

runTime: true 时,它需要先关闭依赖预构建, 不然在依赖预构建的扫描依赖的过程中 esbuild 就会因为 loader 配置错误而报错, 根本就走不到插件调用这里来。

关闭依赖预构建带来的影响就是开发时速度变慢,而且还有可能出现不同的模块风格带来的兼容性问题。

参考: vite 依赖与构建。

bin

不建议使用,有风险,这是兜底方案,最好还是通过 git 查看记录进行还原。

当你选择 runTime: false 时, 会对硬盘上的实际文件进行修改,其实这个操作是有些危险的,这里我们提供的一个还原命令:

npx vue-auto-script-jsx --reset

原理是我们会将对文件进行修改的操作记录缓存起来,然后当你输入这个命令时,它会该插件将修改过的文件进行还原。

缓存文件

缓存文件的路径为:${projectRoot}/node_modules/.vue-auto-script-jsx/cache.json

存储的内容:

{ 文件路径: 文件修改的历史记录 }

这里可以发现,文件修改历史记录是一个数组,那么第一个就是原始文件内容。

{
  "xxx/test1.vue": [
    "<script>\nexport default {\n  setup() {\n    const dom = <h2 id=\"contain-jsx-test1\">contain-jsx-test1</h2>;\n    return () => {\n      return <div>{dom}</div>;\n    };\n  },\n};\n</script>\n",
    "<script lang=\"jsx\">\nexport default {\n  setup() {\n    const dom = <h2 id=\"contain-jsx-test1\">contain-jsx-test1</h2>;\n    return () => {\n      return <div>{dom}</div>;\n    };\n  },\n};\n</script>\n",
    "<script>\nexport default {\n  setup() {\n    const dom = <h2 id=\"contain-jsx-test1\">contain-jsx-test1</h2>;\n    return () => {\n      return <div>{dom}</div>;\n    };\n  },\n};\n</script>\n"
  ],
  "xxx/test2.vue": [
    "<script>\nexport default {\n  render() {\n    return (\n      <div>\n        <h2 id=\"contain-jsx-test2\">contain-jsx-test2</h2>\n      </div>\n    );\n  },\n};\n</script>\n",
    "<script lang=\"jsx\">\nexport default {\n  render() {\n    return (\n      <div>\n        <h2 id=\"contain-jsx-test2\">contain-jsx-test2</h2>\n      </div>\n    );\n  },\n};\n</script>\n",
    "<script>\nexport default {\n  render() {\n    return (\n      <div>\n        <h2 id=\"contain-jsx-test2\">contain-jsx-test2</h2>\n      </div>\n    );\n  },\n};\n</script>\n"
  ]
}

License

MIT License © 2022-PRESENT Mr-xzq

Package Sidebar

Install

npm i plugin-auto-script-jsx

Weekly Downloads

3

Version

1.0.0-beta.2

License

MIT

Unpacked Size

198 kB

Total Files

9

Last publish

Collaborators

  • mr-xzq