index.mjs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import { createFilter } from '@rollup/pluginutils';
  2. import * as changeCase from 'change-case';
  3. import { init, parse } from 'es-module-lexer';
  4. import MagicString from 'magic-string';
  5. import path from 'pathe';
  6. import consola from 'consola';
  7. import { normalizePath } from 'vite';
  8. import fs from 'fs-extra';
  9. import { createRequire } from 'module';
  10. function resolveNodeModules(libName, ...dir) {
  11. const esRequire = createRequire(import.meta.url);
  12. let modulePath = "";
  13. try {
  14. modulePath = normalizePath(esRequire.resolve(libName));
  15. } catch (error) {
  16. modulePath = normalizePath(require.resolve(libName));
  17. }
  18. const lastIndex = modulePath.lastIndexOf(libName);
  19. return normalizePath(path.resolve(modulePath.substring(0, lastIndex), ...dir));
  20. }
  21. function resolvePnp(module) {
  22. try {
  23. return normalizePath(require.resolve(module));
  24. } catch (error) {
  25. return "";
  26. }
  27. }
  28. const isPnp = !!process.versions.pnp;
  29. function isRegExp(value) {
  30. return Object.prototype.toString.call(value) === "[object RegExp]";
  31. }
  32. function fileExists(f) {
  33. try {
  34. fs.accessSync(f, fs.constants.W_OK);
  35. return true;
  36. } catch (error) {
  37. return false;
  38. }
  39. }
  40. function isFunction(value) {
  41. return value != null && Object.prototype.toString.call(value) === "[object Function]";
  42. }
  43. function AntdResolve() {
  44. return {
  45. libraryName: "antd",
  46. esModule: true,
  47. resolveStyle: (name) => {
  48. return `antd/es/${name}/style/index`;
  49. }
  50. };
  51. }
  52. function AndDesignVueResolve() {
  53. return {
  54. ensureStyleFile: true,
  55. libraryName: "ant-design-vue",
  56. esModule: true,
  57. resolveStyle: (name) => {
  58. return `ant-design-vue/es/${name}/style/index`;
  59. }
  60. };
  61. }
  62. function ElementPlusResolve() {
  63. return {
  64. libraryName: "element-plus",
  65. ensureStyleFile: true,
  66. esModule: true,
  67. resolveStyle: (name) => {
  68. return `element-plus/theme-chalk/${name}.css`;
  69. },
  70. base: "element-plus/theme-chalk/base.css"
  71. };
  72. }
  73. function VantResolve() {
  74. return {
  75. libraryName: "vant",
  76. esModule: true,
  77. resolveStyle: (name) => {
  78. return `vant/es/${name}/style`;
  79. }
  80. };
  81. }
  82. function NutuiResolve() {
  83. return {
  84. libraryName: "@nutui/nutui",
  85. libraryNameChangeCase: "pascalCase",
  86. resolveStyle: (name) => {
  87. return `@nutui/nutui/dist/packages/${name}/index.scss`;
  88. }
  89. };
  90. }
  91. function VxeTableResolve() {
  92. return {
  93. ensureStyleFile: true,
  94. libraryName: "vxe-table",
  95. esModule: true,
  96. resolveStyle: (name) => {
  97. return `vxe-table/es/${name}/style.css`;
  98. }
  99. };
  100. }
  101. const ensureFileExts = [".css", ".js", ".scss", ".less", ".styl"];
  102. const asRE = /\s+as\s+\w+,?/g;
  103. consola.wrapConsole();
  104. function createStyleImportPlugin(options) {
  105. const {
  106. include = ["**/*.vue", "**/*.ts", "**/*.js", "**/*.tsx", "**/*.jsx"],
  107. exclude = "node_modules/**",
  108. resolves = []
  109. } = options;
  110. let { libs = [] } = options;
  111. libs = [...libs, ...resolves];
  112. const filter = createFilter(include, exclude);
  113. let needSourcemap = false;
  114. let external;
  115. return {
  116. name: "vite:style-import",
  117. enforce: "post",
  118. configResolved(resolvedConfig) {
  119. needSourcemap = !!resolvedConfig.build.sourcemap;
  120. external = resolvedConfig?.build?.rollupOptions?.external ?? void 0;
  121. },
  122. async transform(code, id) {
  123. if (!code || !filter(id) || !needTransform(code, libs)) {
  124. return null;
  125. }
  126. await init;
  127. let imports = [];
  128. try {
  129. imports = parse(code)[0];
  130. } catch (e) {
  131. consola.error(e);
  132. }
  133. if (!imports.length) {
  134. return null;
  135. }
  136. let s;
  137. const str = () => s || (s = new MagicString(code));
  138. for (let index = 0; index < imports.length; index++) {
  139. const { n, se, ss } = imports[index];
  140. if (!n)
  141. continue;
  142. const lib = getLib(n, libs, external);
  143. if (!lib)
  144. continue;
  145. const importStr = code.slice(ss, se);
  146. let importVariables = transformImportVar(importStr);
  147. importVariables = filterImportVariables(importVariables, lib.importTest);
  148. const importCssStrList = await transformComponentCss(lib, importVariables);
  149. const compStrList = [];
  150. const { base = "" } = lib;
  151. let baseImporter = base ? `
  152. import '${base}'` : "";
  153. if (str().toString().includes(base)) {
  154. baseImporter = "";
  155. }
  156. const endIndex = se + 1;
  157. str().prependRight(endIndex, `${baseImporter}
  158. ${compStrList.join("")}${importCssStrList.join("")}`);
  159. }
  160. return {
  161. map: needSourcemap ? str().generateMap({ hires: true }) : null,
  162. code: str().toString()
  163. };
  164. }
  165. };
  166. }
  167. function filterImportVariables(importVars, reg) {
  168. if (!reg) {
  169. return importVars;
  170. }
  171. return importVars.filter((item) => reg.test(item));
  172. }
  173. async function transformComponentCss(lib, importVariables) {
  174. const {
  175. libraryName,
  176. resolveStyle,
  177. esModule,
  178. libraryNameChangeCase = "paramCase",
  179. ensureStyleFile = false
  180. } = lib;
  181. if (!isFunction(resolveStyle) || !libraryName) {
  182. return [];
  183. }
  184. const set = /* @__PURE__ */ new Set();
  185. for (let index = 0; index < importVariables.length; index++) {
  186. const name = getChangeCaseFileName(importVariables[index], libraryNameChangeCase);
  187. let importStr = resolveStyle(name);
  188. if (!importStr) {
  189. continue;
  190. }
  191. let isAdd = true;
  192. if (isPnp) {
  193. importStr = resolvePnp(importStr);
  194. isAdd = !!importStr;
  195. } else {
  196. if (esModule) {
  197. importStr = resolveNodeModules(libraryName, importStr);
  198. }
  199. if (ensureStyleFile) {
  200. isAdd = ensureFileExists(libraryName, importStr, esModule);
  201. }
  202. }
  203. isAdd && set.add(`import '${importStr}';
  204. `);
  205. }
  206. return Array.from(set);
  207. }
  208. function transformImportVar(importStr) {
  209. if (!importStr) {
  210. return [];
  211. }
  212. const exportStr = importStr.replace("import", "export").replace(asRE, ",");
  213. let importVariables = [];
  214. try {
  215. importVariables = parse(exportStr)[1];
  216. } catch (error) {
  217. consola.error(error);
  218. }
  219. return importVariables;
  220. }
  221. function ensureFileExists(libraryName, importStr, esModule = false) {
  222. const extName = path.extname(importStr);
  223. if (!extName) {
  224. return tryEnsureFile(libraryName, importStr, esModule);
  225. }
  226. if (esModule) {
  227. return fileExists(importStr);
  228. }
  229. return true;
  230. }
  231. function tryEnsureFile(libraryName, filePath, esModule = false) {
  232. const filePathList = ensureFileExts.map((item) => {
  233. const p = `${filePath}${item}`;
  234. return esModule ? p : resolveNodeModules(libraryName, p);
  235. });
  236. return filePathList.some((item) => fileExists(item));
  237. }
  238. function getLib(libraryName, libs, external) {
  239. let libList = libs;
  240. if (external) {
  241. const isString = typeof external === "string";
  242. const isRE = isRegExp(external);
  243. if (isString) {
  244. libList = libList.filter((item) => item.libraryName !== external);
  245. } else if (isRE) {
  246. libList = libList.filter((item) => !external.test(item.libraryName));
  247. } else if (Array.isArray(external)) {
  248. libList = libList.filter((item) => {
  249. return !external.some((val) => {
  250. if (typeof val === "string") {
  251. return val === item.libraryName;
  252. }
  253. return val.test(item.libraryName);
  254. });
  255. });
  256. }
  257. }
  258. return libList.find((item) => item.libraryName === libraryName);
  259. }
  260. function getChangeCaseFileName(importedName, libraryNameChangeCase) {
  261. try {
  262. return changeCase[libraryNameChangeCase](importedName);
  263. } catch (error) {
  264. return importedName;
  265. }
  266. }
  267. function needTransform(code, libs) {
  268. return !libs.every(({ libraryName }) => {
  269. return !new RegExp(`('${libraryName}')|("${libraryName}")`).test(code);
  270. });
  271. }
  272. export { AndDesignVueResolve, AntdResolve, ElementPlusResolve, NutuiResolve, VantResolve, VxeTableResolve, createStyleImportPlugin, getChangeCaseFileName, transformImportVar };