├── .gitignore ├── .travis.yml ├── build.bat ├── package.json ├── publish.bat ├── readme-zh.md ├── readme.md ├── src └── index.ts ├── start.bat ├── tsconfig.json └── version-log.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # See https://help.github.com/ignore-files/ for more about ignoring files. 3 | 4 | # dependencies 5 | node_modules 6 | 7 | # builds 8 | build 9 | dist 10 | .rpt2_cache 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 9 4 | - 8 5 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | yarn build 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-plugin-require", 3 | "version": "1.2.14", 4 | "description": "can let vite projects to support require", 5 | "author": "wangzongming", 6 | "license": "MIT", 7 | "repository": "https://github.com/wangzongming/vite-plugin-require", 8 | "main": "dist/index.js", 9 | "typings": "dist/index.d.ts", 10 | "engines": { 11 | "node": ">=8", 12 | "npm": ">=5" 13 | }, 14 | "scripts": { 15 | "start": "tsc -w -p .", 16 | "build": "tsc -p ." 17 | }, 18 | "peerDependencies": { 19 | "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" 20 | }, 21 | "dependencies": { 22 | "@babel/generator": "^7.24.5", 23 | "@babel/parser": "^7.24.5", 24 | "@babel/traverse": "^7.24.5", 25 | "@babel/types": "^7.24.5", 26 | "@vue/compiler-sfc": "^3.4.27", 27 | "vue-loader": "^17.4.2", 28 | "webpack": "^4.46.0 || ^5.0.0" 29 | }, 30 | "files": [ 31 | "dist" 32 | ], 33 | "keywords": [ 34 | "vite", 35 | "require", 36 | "vite-plugin", 37 | "vite-plugin-require" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /publish.bat: -------------------------------------------------------------------------------- 1 | yarn publish 2 | -------------------------------------------------------------------------------- /readme-zh.md: -------------------------------------------------------------------------------- 1 | # vite-plugin-require [![npm](https://img.shields.io/npm/v/vite-plugin-require.svg)](https://www.npmjs.com/package/vite-plugin-require) [![npm](https://img.shields.io/npm/dm/vite-plugin-require.svg?style=flat)](https://www.npmjs.com/package/vite-plugin-require) 2 | 3 | 4 | 5 | > 让 vite 项目无痛支持 `require("xxx")` [vite-plugin-require](https://www.npmjs.com/package/vite-plugin-require) 6 | 7 | 安装即可实现项目支持 `require` 语法,大部分情况下无需配置。 8 | 9 | **如果项目对你有用的话,请点个star吧!** 10 | 11 | --- 12 | - [中文文档](https://github.com/wangzongming/vite-plugin-require/blob/master/readme-zh.md) 13 | - [English](https://github.com/wangzongming/vite-plugin-require) 14 | --- 15 | 16 | ## 交流群 17 | 18 | QQ 交流群: 854445223 19 | 20 | ## 适配的 vite 版本 21 | 22 | - √ vite2 23 | - √ vite3 24 | - √ vite4 25 | - √ vite5 26 | 27 | --- 28 | 29 | ## 安装 30 | 31 | ``` 32 | npm i vite-plugin-require | yarn add vite-plugin-require 33 | ``` 34 | 35 | --- 36 | ## 使用 37 | 38 | ```js 39 | import vue from '@vitejs/plugin-vue' 40 | import vitePluginRequire from "vite-plugin-require"; 41 | 42 | export default { 43 | plugins: [ 44 | vue(), 45 | 46 | // 必须放在 vue 插件后面 47 | vitePluginRequire(), 48 | 49 | // vite4、vite5 需要像下面这样写 50 | // vitePluginRequire.default() 51 | ], 52 | }; 53 | ``` 54 | --- 55 | ## 配置项 56 | 57 | 两个选项,这在大多数情况下不是必需的 58 | 59 | ### fileRegex 60 | 61 | 需要转换的文件,默认配置:/(.jsx?|.tsx?|.vue)$/ 62 | 63 | ``` js 64 | vitePluginRequire({ fileRegex:/(.jsx?|.tsx?|.vue)$/ }) 65 | ``` 66 | 67 | 68 | ### translateType 69 | 70 | 转换模式。默认模式为“import”。 71 | 72 | "import" 就是寻常的资源导入 73 | 74 | "importMetaUrl" see https://vitejs.cn/guide/assets.html#new-url-url-import-meta-url 75 | 76 | ``` js 77 | vitePluginRequire({ translateType: "import" }) 78 | ``` 79 | 80 | `translateType: "import"` 81 | 82 | 默认情况下,插件将所有 `require` 引用路径复制顶部,并使用 `import` 导入它们。 83 | 84 | 85 | `translateType: "importMetaUrl"` 86 | 87 | 在这种模式下, 插件使用 ` import.meta.url ` 去转换 `require` 。 88 | 89 | 因此,该模式可以实现按需加载。例如: 90 | ``` 91 | let imgUrl = process.env.NODE_ENV !== "development" ? require("../imgs/logo.png") : null; 92 | 93 | // some code... 94 | ``` 95 | 96 | ps: `translateType: "importMetaUrl"` 在这种模式下,代码不会被删除。 97 | 98 | 只能满足如下要求: https://github.com/wangzongming/vite-plugin-require/issues/28 99 | 100 | ``` 101 | 注意注意注意:imgUrl 存在才进行渲染 img ,一定需要是这个顺序。而不是在 src 中进行判断,如:src={xx ? require("../imgs/logo.png") : null} 102 | 103 | let imgUrl = process.env.NODE_ENV !== "development" ? require("../imgs/logo.png") : null; 104 | 105 | return <> 106 | { imgUrl ? : null } 107 | 108 | 109 | ``` 110 | 111 | ## 根目录在哪里? 112 | 113 | 整个项目目录是根目录。 114 | 对于资源你怎么引用并不重要。 115 | 116 | --- 117 | ## 案例 118 | 119 | 假设 src 目录下有 app.jsx 和 imgs 文件夹 120 | 121 | ```jsx 122 | // app.jsx 123 | function App() { 124 | // 变量必须放置到最上面 125 | // 并且不可以使用字符串模板 126 | const img2 = "./img/1.png"; 127 | const img3_1 = "./img/"; 128 | const img3_2 = "./1/"; 129 | 130 | return ( 131 |
132 | 133 | logo1 134 | 135 | logo1 136 | 137 | logo1 138 |
139 | ); 140 | } 141 | export default App; 142 | ``` 143 | --- 144 | ## 升级日志 145 | 146 | *保证每次的升级都不是破坏性的 147 | 148 | https://github.com/wangzongming/vite-plugin-require/blob/master/version-log.md 149 | 150 | --- 151 | ## 复杂目录嵌套 152 | 153 | 图片1: src/imgs/logo.png 154 | 155 | 图片2:src/views/Page1/imgs/logo.png 156 | 157 | ```jsx 158 | // src/views/Page1/index.jsx 159 | function Page() { 160 | return ( 161 |
162 | 163 | logo1 164 | 165 | 166 | logo1 167 |
168 | ); 169 | } 170 | export default Page; 171 | ``` 172 | --- 173 | 174 | 175 | ## 别名设置 176 | 177 | vite.config.js 178 | 179 | ``` 180 | resolve: { 181 | alias: [ 182 | { find: "@imgs", replacement: path.resolve(__dirname, "./src/imgs/") }, 183 | ], 184 | }, 185 | ``` 186 | 187 | page.jsx 188 | 189 | ``` 190 | 191 | ``` 192 | --- 193 | ## FAQ 194 | 195 | ### 1、vitePluginRequire is not a function 196 | 197 | ```js 198 | import vitePluginRequire from "vite-plugin-require"; 199 | 200 | export default { 201 | plugins: [ 202 | vitePluginRequire.default() 203 | ], 204 | }; 205 | ``` 206 | 207 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # vite-plugin-require [![npm](https://img.shields.io/npm/v/vite-plugin-require.svg)](https://www.npmjs.com/package/vite-plugin-require) [![npm](https://img.shields.io/npm/dm/vite-plugin-require.svg?style=flat)](https://www.npmjs.com/package/vite-plugin-require) 2 | 3 | > can let vite projects to support `require` [vite-plugin-require](https://www.npmjs.com/package/vite-plugin-require) 4 | 5 | Install and use to achieve painless support `require("xxx")` 6 | 7 | 8 | **If the project is useful to you, please click on star!** 9 | 10 | 11 | 12 | --- 13 | - [中文文档](https://github.com/wangzongming/vite-plugin-require/blob/master/readme-zh.md) 14 | - [English](https://github.com/wangzongming/vite-plugin-require) 15 | --- 16 | 17 | ## 其他作品 18 | 19 | 硬件接入AI最简单、最低成本的方案: https://github.com/wangzongming/esp-ai 20 | 21 | ## 交流群 22 | 23 | QQ 交流群: 854445223 24 | 25 | ## Adaptive 26 | 27 | - √ vite2 28 | - √ vite3 29 | - √ vite4 30 | - √ vite5 31 | 32 | --- 33 | ## Install 34 | 35 | ``` 36 | npm i vite-plugin-require | yarn add vite-plugin-require 37 | ``` 38 | 39 | 40 | --- 41 | ## Usage 42 | 43 | ```js 44 | import vue from '@vitejs/plugin-vue' 45 | import vitePluginRequire from "vite-plugin-require"; 46 | 47 | export default { 48 | plugins: [ 49 | vue(), 50 | 51 | // Must be placed after the vue plugin 52 | vitePluginRequire(), 53 | 54 | // vite4、vite5 55 | // vitePluginRequire.default() 56 | ], 57 | }; 58 | ``` 59 | --- 60 | ## options 61 | 62 | Two options,which is not required in most cases 63 | 64 | #### fileRegex 65 | 66 | File to be converted, default configuration: /(.jsx? |.tsx? |.vue)$/ 67 | 68 | ``` js 69 | vitePluginRequire({ fileRegex:/(.jsx?|.tsx?|.vue)$/ }) 70 | ``` 71 | 72 | 73 | #### translateType 74 | 75 | Conversion mode. The default mode is "import" 76 | 77 | 78 | "import" is resource introductio 79 | 80 | "importMetaUrl" see https://vitejs.cn/guide/assets.html#new-url-url-import-meta-url 81 | 82 | ``` js 83 | vitePluginRequire({ translateType: "import" }) 84 | ``` 85 | 86 | 87 | `translateType: "import"` 88 | 89 | By default, plug-ins place all `require` references at the top and import them using import. 90 | 91 | 92 | `translateType: "importMetaUrl"` 93 | In this mode, the plugin uses ` import.meta.url ` instead of`require` 94 | Therefore, on-demand loading can be implemented in this mode. eg: 95 | ``` 96 | let imgUrl = process.env.NODE_ENV !== "development" ? require("../imgs/logo.png") : null; 97 | 98 | // some code... 99 | ``` 100 | 101 | ps: `translateType: "importMetaUrl"` Code is not deleted in mode。 102 | 103 | Only the following requirements can be implemented. 104 | 105 | detail see: https://github.com/wangzongming/vite-plugin-require/issues/28 106 | ``` 107 | let imgUrl = process.env.NODE_ENV !== "development" ? require("../imgs/logo.png") : null; 108 | 109 | return <> 110 | { imgUrl ? : null } 111 | 112 | 113 | ``` 114 | 115 | 116 | ## Where is the root directory? 117 | 118 | The entire project directory is the root directory。 119 | It doesn't matter how you quote it. 120 | 121 | --- 122 | ## Demo 123 | 124 | Suppose there are app.jsx and imgs folders in the src directory 125 | 126 | ```jsx 127 | // app.jsx 128 | function App() { 129 | // The variable must be placed on the top 变量必须放置到最上面 130 | // Do not use string templates 不可以使用字符串模板 131 | 132 | const img2 = "./img/1.png"; 133 | const img3_1 = "./img/"; 134 | const img3_2 = "./1/"; 135 | 136 | return ( 137 |
138 | 139 | logo1 140 | 141 | logo1 142 | 143 | logo1 144 |
145 | ); 146 | } 147 | export default App; 148 | ``` 149 | --- 150 | ## Upgrade log 151 | 152 | https://github.com/wangzongming/vite-plugin-require/blob/master/version-log.md 153 | 154 | --- 155 | ## Other deeper subdirectories 156 | img1: src/imgs/logo.png 157 | 158 | img2:src/views/Page1/imgs/logo.png 159 | 160 | 161 | ```jsx 162 | // src/views/Page1/index.jsx 163 | function Page() { 164 | return ( 165 |
166 | 167 | logo1 168 | 169 | 170 | logo1 171 |
172 | ); 173 | } 174 | export default Page; 175 | ``` 176 | --- 177 | 178 | ## Alias 179 | 180 | vite.config.js 181 | 182 | ``` 183 | resolve: { 184 | alias: [ 185 | { find: "@imgs", replacement: path.resolve(__dirname, "./src/imgs/") }, 186 | ], 187 | }, 188 | ``` 189 | 190 | page.jsx 191 | 192 | ``` 193 | 194 | ``` 195 | --- 196 | ## FAQ 197 | 198 | ### 1、vitePluginRequire is not a function 199 | 200 | ```js 201 | import vitePluginRequire from "vite-plugin-require"; 202 | 203 | export default { 204 | plugins: [ 205 | vitePluginRequire.default() 206 | ], 207 | }; 208 | ``` 209 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import { parse } from '@babel/parser'; 3 | import traverse from "@babel/traverse"; 4 | import generate from "@babel/generator"; 5 | import { importDeclaration, importDefaultSpecifier, stringLiteral, identifier, newExpression, expressionStatement, memberExpression, BinaryExpression, ExpressionStatement, Node } from "@babel/types"; 6 | 7 | export default function vitePluginRequire(opts?: { 8 | fileRegex?: RegExp; 9 | log?: (...arg: any[]) => void; 10 | // 转换方式,默认用 import 方式替换,可使用 https://vitejs.cn/guide/assets.html#new-url-url-import-meta-url 方式 11 | // importMetaUrl | import 12 | translateType?: "importMetaUrl" | "import"; 13 | }) { 14 | const { fileRegex = /(.jsx?|.tsx?|.vue)$/, log, translateType = "import" } = opts || {}; 15 | let sourcemap: boolean; 16 | return { 17 | name: "vite-plugin-require", 18 | configResolved(resolvedConfig: any) { 19 | // dev model default true 20 | const isDev = resolvedConfig.env.MODE === "development"; 21 | if (isDev) { 22 | sourcemap = true; 23 | } else { 24 | sourcemap = resolvedConfig.build.sourcemap as boolean; 25 | } 26 | }, 27 | async transform(code: string, id: string) { 28 | // Exclude files in node_modules 29 | if (/\/node_modules\//g.test(id)) return; 30 | let newCode = code; 31 | let newMap = null; // 没有更改源代码时为 null 32 | if (fileRegex.test(id)) { 33 | const isVueFile: Boolean = /(.vue)$/.test(id); 34 | let plugins = isVueFile ? [[require("vue-loader")]] : ["jsx"]; 35 | 36 | const ast = parse(code, { 37 | sourceType: "module", 38 | // 更新版本的 babel/parse 只能配置为二维数组,第二个选项为配置 39 | plugins: plugins as any, 40 | }) as any; 41 | 42 | traverse(ast, { 43 | enter(path) { 44 | if (path.isIdentifier({ name: "require" })) { 45 | const arg = (path.container as Record)?.arguments?.[0]; 46 | 47 | if (arg) { 48 | let stringVal: string = ""; 49 | switch (arg?.type) { 50 | case "StringLiteral": 51 | stringVal = arg.value; 52 | break; 53 | case "Identifier": 54 | const IdentifierName = arg.name; 55 | traverse(ast, { 56 | Identifier: (path) => { 57 | // 这里不处理各种变量赋值,只考虑唯一变量 58 | if (path.node.name === IdentifierName) { 59 | if (!Array.isArray(path.container) && (path.container as any).init?.type === "StringLiteral") { 60 | stringVal = (path.container as any).init.value; 61 | } 62 | } 63 | }, 64 | }); 65 | break; 66 | case "BinaryExpression": 67 | const binaryExpressionLoopFn = (lOr: BinaryExpression["right"] | BinaryExpression["left"]) => { 68 | if (lOr.type === "BinaryExpression") { 69 | binaryExpressionLoopFn(lOr.left); 70 | binaryExpressionLoopFn(lOr.right); 71 | } else { 72 | // 只处理变量或者字符串 73 | if (lOr.type === "StringLiteral") { 74 | stringVal += lOr.value; 75 | } else if (lOr.type === "Identifier") { 76 | // 这里不处理各种变量赋值,只考虑唯一变量 77 | const IdentifierName = lOr.name; 78 | traverse(ast, { 79 | Identifier: (path) => { 80 | // 这里不处理各种变量赋值,只考虑唯一变量 81 | if (path.node.name === IdentifierName) { 82 | // log(path); 83 | if (!Array.isArray(path.container) && (path.container as any).init?.type === "StringLiteral") { 84 | // log((path.container as any).init.value); 85 | stringVal += (path.container as any).init.value; 86 | } 87 | } 88 | }, 89 | }); 90 | } else if (lOr.type === "MemberExpression") { 91 | // 这里不处理各种变量赋值,只考虑唯一变量 92 | if (lOr.property.type === "Identifier") { 93 | const IdentifierName = lOr.property.name; 94 | traverse(ast, { 95 | Identifier: (path) => { 96 | // 这里不处理各种变量赋值,只考虑唯一变量 97 | if (path.node.name === IdentifierName) { 98 | // log(path) 99 | if (!Array.isArray(path.container) && (path.container as any).init?.type === "StringLiteral") { 100 | // log((path.container as any).init.value); 101 | stringVal += (path.container as any).init.value; 102 | } 103 | } 104 | }, 105 | }); 106 | } 107 | } else { 108 | throw `不支持的: BinaryExpression 组成类型 ${lOr.type}`; 109 | } 110 | } 111 | }; 112 | binaryExpressionLoopFn(arg.left); 113 | binaryExpressionLoopFn(arg.right); 114 | break; 115 | case "MemberExpression": 116 | // requre(new Url()) 117 | break; 118 | default: 119 | throw `Unsupported type: ${arg?.type}`; 120 | } 121 | path.node.name = ""; 122 | if (stringVal) { 123 | // Insert import at the top to pack resources when vite packs 124 | let realPath: string | ExpressionStatement = `vitePluginRequire_${new Date().getTime()}_${parseInt(Math.random() * 100000000 + 100 + "")}`; 125 | if (translateType === "import") { 126 | const importAst = importDeclaration([importDefaultSpecifier(identifier(realPath))], stringLiteral(stringVal as string)); 127 | ast.program.body.unshift(importAst as any); 128 | 129 | switch (arg?.type) { 130 | case "StringLiteral": 131 | (path.container as Record).arguments[0].value = realPath; 132 | if ((path.container as Record).arguments[0].extra) { 133 | (path.container as Record).arguments[0].extra.raw = realPath; 134 | (path.container as Record).arguments[0].extra.rawValue = realPath; 135 | } 136 | break; 137 | case "Identifier": 138 | (path.container as Record).arguments[0].name = realPath; 139 | break; 140 | case "BinaryExpression": 141 | // 直接改成变量 142 | (path.container as Record).arguments[0] = identifier(realPath); 143 | break; 144 | default: 145 | throw `Unsupported type: ${arg?.type}`; 146 | } 147 | } else if (translateType === "importMetaUrl") { 148 | // 改为 import.meta.url ... 149 | const metaObj = memberExpression(memberExpression(identifier("import"), identifier("meta")), identifier("url")); 150 | const importAst = newExpression(identifier("URL"), [stringLiteral(stringVal), metaObj]); 151 | const hrefObj = expressionStatement(memberExpression(importAst, identifier("href"))); 152 | const strCode = generate(hrefObj as any, {}).code.replace(/\;$/, ''); 153 | // log("importAst", strCode); 154 | 155 | switch (arg?.type) { 156 | case "StringLiteral": 157 | (path.container as Record).arguments[0].value = strCode; 158 | if ((path.container as Record).arguments[0].extra) { 159 | (path.container as Record).arguments[0].extra.raw = strCode; 160 | (path.container as Record).arguments[0].extra.rawValue = strCode; 161 | } 162 | break; 163 | case "Identifier": 164 | (path.container as Record).arguments[0].name = strCode; 165 | break; 166 | case "BinaryExpression": 167 | // 直接改成变量 168 | (path.container as Record).arguments[0] = identifier(strCode); 169 | break; 170 | default: 171 | throw `Unsupported type: ${arg?.type}`; 172 | } 173 | } 174 | } 175 | } 176 | } 177 | }, 178 | }); 179 | 180 | const output = generate(ast, { 181 | sourceMaps: true, 182 | // sourceFileName: code 183 | sourceFileName: id 184 | }, code); 185 | newCode = output.code; 186 | if (sourcemap) { 187 | newMap = output.map; 188 | } 189 | } 190 | return { 191 | code: newCode, 192 | // https://rollupjs.org/guide/en/#thisgetcombinedsourcemap 193 | map: newMap, 194 | }; 195 | }, 196 | }; 197 | } 198 | -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | yarn start 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "workers": 4, 3 | "async": false, 4 | "useTypescriptIncrementalApi": true, 5 | "watch": ["src"], 6 | "isolatedModules": false, 7 | "compilerOptions": { 8 | "jsx": "preserve", 9 | "module": "commonjs", 10 | "lib": ["es2018", "DOM"], 11 | "outDir": "dist", 12 | "noImplicitAny": true, 13 | "removeComments": true, 14 | "preserveConstEnums": true, 15 | "sourceMap": true, 16 | "skipLibCheck": true, 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": false, 20 | "declaration": true, 21 | "typeRoots": ["node_modules/@types"], 22 | "types": ["node"] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /version-log.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ##### 1.2.14 5 | 6 | Dependent upgrade 7 | 8 | ##### 1.1.14 9 | 10 | fix https://github.com/wangzongming/vite-plugin-require/issues/35 11 | 12 | ##### 1.1.13 13 | 14 | fix some bug 15 | 16 | ##### 1.1.12 17 | 18 | fix https://github.com/wangzongming/vite-plugin-require/issues/35 19 | 20 | ##### 1.1.11 21 | 22 | fix https://github.com/wangzongming/vite-plugin-require/issues/33 23 | 24 | ##### 1.0.0 25 | 26 | fix https://github.com/wangzongming/vite-plugin-require/issues/10 27 | 28 | Support variable、 String splicing 29 | 30 | ##### 0.0.8 31 | 32 | Support vite.config.ts 33 | 34 | ##### 0.0.7 35 | 36 | Fixed incorrect address after packing 37 | Fix react project compiling files in node_modules folder 38 | 39 | ##### 0.0.5 40 | 41 | Fixed Other require keywords will be replaced 42 | Support .vue file 43 | 44 | ##### 0.0.3 45 | 46 | Fixed ../ Case of miscalculation 47 | 48 | ##### 0.0.2 49 | 50 | Fixed not reporting errors to parameters 51 | 52 | ##### 0.0.1 53 | 54 | 发布 --------------------------------------------------------------------------------