版博士V2.0程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

374 line
12 KiB

  1. import {
  2. __spreadValues,
  3. presets
  4. } from "./chunk-7TX24XEC.js";
  5. // src/core/unplugin.ts
  6. import minimatch from "minimatch";
  7. import { slash as slash2 } from "@antfu/utils";
  8. import { createUnplugin } from "unplugin";
  9. // src/core/ctx.ts
  10. import { dirname, isAbsolute, posix, relative, resolve, sep } from "path";
  11. import { promises as fs } from "fs";
  12. import { slash, throttle, toArray as toArray2 } from "@antfu/utils";
  13. import { createFilter } from "@rollup/pluginutils";
  14. import { isPackageExists } from "local-pkg";
  15. import { createUnimport, resolvePreset, scanDirExports } from "unimport";
  16. import { vueTemplateAddon } from "unimport/addons";
  17. import MagicString from "magic-string";
  18. // src/core/eslintrc.ts
  19. function generateESLintConfigs(imports, eslintrc) {
  20. const eslintConfigs = { globals: {} };
  21. imports.map((i) => {
  22. var _a;
  23. return (_a = i.as) != null ? _a : i.name;
  24. }).filter(Boolean).sort().forEach((name) => {
  25. eslintConfigs.globals[name] = eslintrc.globalsPropValue || true;
  26. });
  27. const jsonBody = JSON.stringify(eslintConfigs, null, 2);
  28. return jsonBody;
  29. }
  30. // src/core/resolvers.ts
  31. import { toArray } from "@antfu/utils";
  32. function normalizeImport(info, name) {
  33. if (typeof info === "string") {
  34. return {
  35. name: "default",
  36. as: name,
  37. from: info
  38. };
  39. }
  40. if ("path" in info) {
  41. return {
  42. from: info.path,
  43. as: info.name,
  44. name: info.importName,
  45. sideEffects: info.sideEffects
  46. };
  47. }
  48. return __spreadValues({
  49. name,
  50. as: name
  51. }, info);
  52. }
  53. async function firstMatchedResolver(resolvers, fullname) {
  54. let name = fullname;
  55. for (const resolver of resolvers) {
  56. if (typeof resolver === "object" && resolver.type === "directive") {
  57. if (name.startsWith("v"))
  58. name = name.slice(1);
  59. else
  60. continue;
  61. }
  62. const resolved = await (typeof resolver === "function" ? resolver(name) : resolver.resolve(name));
  63. if (resolved)
  64. return normalizeImport(resolved, fullname);
  65. }
  66. }
  67. function resolversAddon(resolvers) {
  68. return {
  69. async matchImports(names, matched) {
  70. if (!resolvers.length)
  71. return;
  72. const dynamic = [];
  73. const sideEffects = [];
  74. await Promise.all([...names].map(async (name) => {
  75. const matchedImport = matched.find((i) => i.as === name);
  76. if (matchedImport) {
  77. if ("sideEffects" in matchedImport)
  78. sideEffects.push(...toArray(matchedImport.sideEffects).map((i) => normalizeImport(i, "")));
  79. return;
  80. }
  81. const resolved = await firstMatchedResolver(resolvers, name);
  82. if (resolved)
  83. dynamic.push(resolved);
  84. if (resolved == null ? void 0 : resolved.sideEffects)
  85. sideEffects.push(...toArray(resolved == null ? void 0 : resolved.sideEffects).map((i) => normalizeImport(i, "")));
  86. }));
  87. if (dynamic.length) {
  88. this.dynamicImports.push(...dynamic);
  89. this.invalidate();
  90. }
  91. if (dynamic.length || sideEffects.length)
  92. return [...matched, ...dynamic, ...sideEffects];
  93. }
  94. };
  95. }
  96. // src/core/ctx.ts
  97. function createContext(options = {}, root = process.cwd()) {
  98. var _a;
  99. const {
  100. dts: preferDTS = isPackageExists("typescript"),
  101. cache: isCache = false
  102. } = options;
  103. const dirs = (_a = options.dirs) == null ? void 0 : _a.map((dir) => resolve(root, dir));
  104. const eslintrc = options.eslintrc || {};
  105. eslintrc.enabled = eslintrc.enabled === void 0 ? false : eslintrc.enabled;
  106. eslintrc.filepath = eslintrc.filepath || "./.eslintrc-auto-import.json";
  107. eslintrc.globalsPropValue = eslintrc.globalsPropValue === void 0 ? true : eslintrc.globalsPropValue;
  108. const resolvers = options.resolvers ? [options.resolvers].flat(2) : [];
  109. const cachePath = isCache === false ? false : resolve(root, typeof isCache === "string" ? isCache : "node_modules/.cache/unplugin-auto-import.json");
  110. const injectAtEnd = options.injectAtEnd !== false;
  111. const unimport = createUnimport({
  112. imports: [],
  113. presets: [],
  114. injectAtEnd,
  115. addons: [
  116. ...options.vueTemplate ? [vueTemplateAddon()] : [],
  117. resolversAddon(resolvers),
  118. {
  119. declaration(dts2) {
  120. return `${`
  121. /* eslint-disable */
  122. /* prettier-ignore */
  123. // @ts-nocheck
  124. // Generated by unplugin-auto-import
  125. ${dts2}`.trim()}
  126. `;
  127. }
  128. }
  129. ]
  130. });
  131. const importsPromise = flattenImports(options.imports).then((imports) => {
  132. var _a2;
  133. if (!imports.length && !resolvers.length)
  134. console.warn("[auto-import] plugin installed but no imports has defined, see https://github.com/antfu/unplugin-auto-import#configurations for configurations");
  135. (_a2 = options.ignore) == null ? void 0 : _a2.forEach((name) => {
  136. const i = imports.find((i2) => i2.as === name);
  137. if (i)
  138. i.disabled = true;
  139. });
  140. return unimport.getInternalContext().replaceImports(imports);
  141. });
  142. const filter = createFilter(
  143. options.include || [/\.[jt]sx?$/, /\.vue$/, /\.vue\?vue/, /\.svelte$/],
  144. options.exclude || [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/]
  145. );
  146. const dts = preferDTS === false ? false : preferDTS === true ? resolve(root, "auto-imports.d.ts") : resolve(root, preferDTS);
  147. async function generateDTS(file) {
  148. await importsPromise;
  149. const dir = dirname(file);
  150. return unimport.generateTypeDeclarations({
  151. resolvePath: (i) => {
  152. if (i.from.startsWith(".") || isAbsolute(i.from)) {
  153. const related = slash(relative(dir, i.from).replace(/\.ts(x)?$/, ""));
  154. return !related.startsWith(".") ? `./${related}` : related;
  155. }
  156. return i.from;
  157. }
  158. });
  159. }
  160. async function generateESLint() {
  161. return generateESLintConfigs(await unimport.getImports(), eslintrc);
  162. }
  163. const writeConfigFilesThrottled = throttle(500, writeConfigFiles, { noLeading: false });
  164. const writeFileThrottled = throttle(500, writeFile, { noLeading: false });
  165. async function writeFile(filePath, content = "") {
  166. await fs.mkdir(dirname(filePath), { recursive: true });
  167. return await fs.writeFile(filePath, content, "utf-8");
  168. }
  169. let lastDTS;
  170. let lastESLint;
  171. async function writeConfigFiles() {
  172. const promises = [];
  173. if (dts) {
  174. promises.push(
  175. generateDTS(dts).then((content) => {
  176. if (content !== lastDTS) {
  177. lastDTS = content;
  178. return writeFile(dts, content);
  179. }
  180. })
  181. );
  182. }
  183. if (eslintrc.enabled && eslintrc.filepath) {
  184. promises.push(
  185. generateESLint().then((content) => {
  186. if (content !== lastESLint) {
  187. lastESLint = `${content}
  188. `;
  189. return writeFile(eslintrc.filepath, content);
  190. }
  191. })
  192. );
  193. }
  194. return Promise.all(promises);
  195. }
  196. async function scanDirs() {
  197. if (dirs == null ? void 0 : dirs.length) {
  198. await unimport.modifyDynamicImports(async (imports) => {
  199. const exports = await scanDirExports(dirs, {
  200. filePatterns: ["*.{tsx,jsx,ts,js,mjs,cjs,mts,cts}"]
  201. });
  202. exports.forEach((i) => i.__source = "dir");
  203. return modifyDefaultExportsAlias([
  204. ...imports.filter((i) => i.__source !== "dir"),
  205. ...exports
  206. ], options);
  207. });
  208. }
  209. writeConfigFilesThrottled();
  210. }
  211. async function getCacheData(cache) {
  212. const str = (await fs.readFile(cache, "utf-8")).trim();
  213. return JSON.parse(str || "{}");
  214. }
  215. async function generateCache() {
  216. if (!cachePath)
  217. return {};
  218. let cacheData = {};
  219. try {
  220. cacheData = await getCacheData(cachePath);
  221. await Promise.allSettled(Object.keys(cacheData).map(async (filePath) => {
  222. try {
  223. const normalizeRoot = root.replaceAll(sep, posix.sep);
  224. await fs.access(posix.join(normalizeRoot, filePath));
  225. } catch (e) {
  226. Reflect.deleteProperty(cacheData, filePath);
  227. }
  228. }));
  229. await writeFile(cachePath, JSON.stringify(cacheData, null, 2));
  230. } catch (e) {
  231. await writeFile(cachePath, "{}");
  232. }
  233. return cacheData;
  234. }
  235. let isInitialCache = false;
  236. const resolveCachePromise = generateCache();
  237. async function updateCacheImports(id, importList) {
  238. if (!cachePath || isInitialCache && !id)
  239. return;
  240. isInitialCache = true;
  241. const cacheData = await resolveCachePromise;
  242. await unimport.modifyDynamicImports(async (imports) => {
  243. if (id && importList) {
  244. const filePath = posix.normalize(posix.relative(root, id));
  245. importList = importList.filter((i) => {
  246. var _a2;
  247. return ((_a2 = i.name) != null ? _a2 : i.as) && i.name !== "default";
  248. });
  249. if (importList.length)
  250. cacheData[filePath] = importList;
  251. else
  252. delete cacheData[filePath];
  253. writeFileThrottled(cachePath, JSON.stringify(cacheData, null, 2));
  254. return imports.concat(importList);
  255. }
  256. return imports.concat(Object.values(cacheData).reduce((p, n) => p.concat(n), []));
  257. });
  258. }
  259. async function transform(code, id) {
  260. await importsPromise;
  261. const s = new MagicString(code);
  262. const res = await unimport.injectImports(s, id);
  263. await updateCacheImports(id, res.imports);
  264. if (!s.hasChanged())
  265. return;
  266. writeConfigFilesThrottled();
  267. return {
  268. code: s.toString(),
  269. map: s.generateMap({ source: id, includeContent: true })
  270. };
  271. }
  272. return {
  273. root,
  274. dirs,
  275. filter,
  276. scanDirs,
  277. updateCacheImports,
  278. writeConfigFiles,
  279. writeConfigFilesThrottled,
  280. transform,
  281. generateDTS,
  282. generateESLint
  283. };
  284. }
  285. async function flattenImports(map) {
  286. const promises = await Promise.all(toArray2(map).map(async (definition) => {
  287. if (typeof definition === "string") {
  288. if (!presets[definition])
  289. throw new Error(`[auto-import] preset ${definition} not found`);
  290. const preset = presets[definition];
  291. definition = typeof preset === "function" ? preset() : preset;
  292. }
  293. if ("from" in definition && "imports" in definition) {
  294. return await resolvePreset(definition);
  295. } else {
  296. const resolved = [];
  297. for (const mod of Object.keys(definition)) {
  298. for (const id of definition[mod]) {
  299. const meta = {
  300. from: mod
  301. };
  302. if (Array.isArray(id)) {
  303. meta.name = id[0];
  304. meta.as = id[1];
  305. } else {
  306. meta.name = id;
  307. meta.as = id;
  308. }
  309. resolved.push(meta);
  310. }
  311. }
  312. return resolved;
  313. }
  314. }));
  315. return promises.flat();
  316. }
  317. function modifyDefaultExportsAlias(imports, options) {
  318. if (options.defaultExportByFilename) {
  319. imports.forEach((i) => {
  320. var _a, _b, _c;
  321. if (i.name === "default")
  322. i.as = (_c = (_b = (_a = i.from.split("/").pop()) == null ? void 0 : _a.split(".")) == null ? void 0 : _b.shift()) != null ? _c : i.as;
  323. });
  324. }
  325. return imports;
  326. }
  327. // src/core/unplugin.ts
  328. var unplugin_default = createUnplugin((options) => {
  329. let ctx = createContext(options);
  330. return {
  331. name: "unplugin-auto-import",
  332. enforce: "post",
  333. transformInclude(id) {
  334. return ctx.filter(id);
  335. },
  336. async transform(code, id) {
  337. return ctx.transform(code, id);
  338. },
  339. async buildStart() {
  340. await Promise.all([
  341. ctx.scanDirs(),
  342. ctx.updateCacheImports()
  343. ]);
  344. await ctx.writeConfigFiles();
  345. },
  346. async buildEnd() {
  347. await ctx.writeConfigFiles();
  348. },
  349. vite: {
  350. async handleHotUpdate({ file }) {
  351. var _a;
  352. if ((_a = ctx.dirs) == null ? void 0 : _a.some((glob) => minimatch(slash2(file), glob)))
  353. await ctx.scanDirs();
  354. },
  355. async configResolved(config) {
  356. if (ctx.root !== config.root) {
  357. ctx = createContext(options, config.root);
  358. await ctx.scanDirs();
  359. }
  360. }
  361. }
  362. };
  363. });
  364. export {
  365. unplugin_default
  366. };