版博士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.

chunk-5FIR3URH.mjs 12 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // src/ts.ts
  2. import { readFile } from "fs/promises";
  3. import { existsSync } from "fs";
  4. import path from "path";
  5. import {
  6. babelParse,
  7. getFileCodeAndLang,
  8. isStaticExpression,
  9. resolveLiteral,
  10. resolveObjectKey
  11. } from "@vue-macros/common";
  12. import { isDeclaration } from "@babel/types";
  13. var tsFileCache = {};
  14. async function getTSFile(filePath) {
  15. if (tsFileCache[filePath])
  16. return tsFileCache[filePath];
  17. const content = await readFile(filePath, "utf-8");
  18. const { code, lang } = getFileCodeAndLang(content, filePath);
  19. const program = babelParse(code, lang);
  20. return tsFileCache[filePath] = {
  21. filePath,
  22. content,
  23. ast: program.body
  24. };
  25. }
  26. function isTSDeclaration(node) {
  27. return isDeclaration(node) && node.type.startsWith("TS");
  28. }
  29. function mergeTSProperties(a, b) {
  30. return {
  31. callSignatures: [...a.callSignatures, ...b.callSignatures],
  32. constructSignatures: [...a.constructSignatures, ...b.constructSignatures],
  33. methods: { ...a.methods, ...b.methods },
  34. properties: { ...a.properties, ...b.properties }
  35. };
  36. }
  37. async function resolveTSProperties({
  38. type,
  39. scope
  40. }) {
  41. switch (type.type) {
  42. case "TSInterfaceBody":
  43. return resolveTypeElements(scope, type.body);
  44. case "TSTypeLiteral":
  45. return resolveTypeElements(scope, type.members);
  46. case "TSInterfaceDeclaration": {
  47. let properties = resolveTypeElements(scope, type.body.body);
  48. if (type.extends) {
  49. const resolvedExtends = (await Promise.all(
  50. type.extends.map(
  51. (node) => node.expression.type === "Identifier" ? resolveTSReferencedType({
  52. scope,
  53. type: node.expression
  54. }) : void 0
  55. )
  56. )).filter(filterValidExtends);
  57. if (resolvedExtends.length > 0) {
  58. const ext = (await Promise.all(
  59. resolvedExtends.map((resolved) => resolveTSProperties(resolved))
  60. )).reduceRight((acc, curr) => mergeTSProperties(acc, curr));
  61. properties = mergeTSProperties(ext, properties);
  62. }
  63. }
  64. return properties;
  65. }
  66. case "TSIntersectionType": {
  67. let properties = {
  68. callSignatures: [],
  69. constructSignatures: [],
  70. methods: {},
  71. properties: {}
  72. };
  73. for (const subType of type.types) {
  74. const resolved = await resolveTSReferencedType({
  75. scope,
  76. type: subType
  77. });
  78. if (!filterValidExtends(resolved))
  79. continue;
  80. properties = mergeTSProperties(
  81. properties,
  82. await resolveTSProperties(resolved)
  83. );
  84. }
  85. return properties;
  86. }
  87. case "TSMappedType": {
  88. const properties = {
  89. callSignatures: [],
  90. constructSignatures: [],
  91. methods: {},
  92. properties: {}
  93. };
  94. if (!type.typeParameter.constraint)
  95. return properties;
  96. const constraint = await resolveTSReferencedType({
  97. type: type.typeParameter.constraint,
  98. scope
  99. });
  100. if (!(constraint == null ? void 0 : constraint.type))
  101. return properties;
  102. const types = constraint.type.type === "TSUnionType" ? constraint.type.types : [constraint.type];
  103. for (const subType of types) {
  104. if (subType.type !== "TSLiteralType")
  105. continue;
  106. const literal = subType.literal;
  107. if (!isStaticExpression(literal))
  108. continue;
  109. const key = resolveLiteral(
  110. literal
  111. );
  112. if (!key)
  113. continue;
  114. properties.properties[String(key)] = {
  115. value: type.typeAnnotation ? { scope, type: type.typeAnnotation } : null,
  116. optional: type.optional === "+" || type.optional === true,
  117. signature: { type, scope }
  118. };
  119. }
  120. return properties;
  121. }
  122. case "TSFunctionType": {
  123. const properties = {
  124. callSignatures: [{ type, scope }],
  125. constructSignatures: [],
  126. methods: {},
  127. properties: {}
  128. };
  129. return properties;
  130. }
  131. default:
  132. throw new Error(`unknown node: ${type == null ? void 0 : type.type}`);
  133. }
  134. function filterValidExtends(node) {
  135. return !isTSExports(node) && [
  136. "TSInterfaceDeclaration",
  137. "TSTypeLiteral",
  138. "TSIntersectionType"
  139. ].includes(node == null ? void 0 : node.type.type);
  140. }
  141. }
  142. function resolveTypeElements(scope, elements) {
  143. var _a;
  144. const properties = {
  145. callSignatures: [],
  146. constructSignatures: [],
  147. methods: {},
  148. properties: {}
  149. };
  150. const tryGetKey = (element) => {
  151. try {
  152. return resolveObjectKey(element.key, element.computed, false);
  153. } catch {
  154. }
  155. };
  156. for (const element of elements) {
  157. switch (element.type) {
  158. case "TSCallSignatureDeclaration":
  159. properties.callSignatures.push({ scope, type: element });
  160. break;
  161. case "TSConstructSignatureDeclaration":
  162. properties.constructSignatures.push({ scope, type: element });
  163. break;
  164. case "TSMethodSignature": {
  165. const key = tryGetKey(element);
  166. if (!key)
  167. continue;
  168. if (properties.properties[key])
  169. continue;
  170. if (!properties.methods[key])
  171. properties.methods[key] = [];
  172. if (element.typeAnnotation) {
  173. properties.methods[key].push({ scope, type: element });
  174. }
  175. break;
  176. }
  177. case "TSPropertySignature": {
  178. const key = tryGetKey(element);
  179. if (!key)
  180. continue;
  181. if (!properties.properties[key] && !properties.methods[key]) {
  182. const type = (_a = element.typeAnnotation) == null ? void 0 : _a.typeAnnotation;
  183. properties.properties[key] = {
  184. value: type ? { type, scope } : null,
  185. optional: !!element.optional,
  186. signature: { type: element, scope }
  187. };
  188. }
  189. break;
  190. }
  191. case "TSIndexSignature":
  192. break;
  193. }
  194. }
  195. return properties;
  196. }
  197. async function resolveTSReferencedType(ref, stacks = []) {
  198. var _a;
  199. const { scope, type } = ref;
  200. if (stacks.some((stack) => stack.scope === scope && stack.type === type)) {
  201. return ref;
  202. }
  203. stacks.push(ref);
  204. if (type.type === "TSTypeAliasDeclaration" || type.type === "TSParenthesizedType") {
  205. return resolveTSReferencedType({ scope, type: type.typeAnnotation }, stacks);
  206. }
  207. let refNames;
  208. if (type.type === "Identifier") {
  209. refNames = [type.name];
  210. } else if (type.type === "TSTypeReference") {
  211. if (type.typeName.type === "Identifier") {
  212. refNames = [type.typeName.name];
  213. } else {
  214. refNames = resolveTSEntityName(type.typeName).map((id) => id.name);
  215. }
  216. } else if (type.type === "TSModuleDeclaration" && type.body.type === "TSModuleBlock") {
  217. return resolveTSExports({ type: type.body, scope });
  218. } else {
  219. return { scope, type };
  220. }
  221. const [refName, ...restNames] = refNames;
  222. const { body, file } = resolveTSScope(scope);
  223. for (let node of body) {
  224. if (node.type === "ImportDeclaration") {
  225. const specifier = node.specifiers.find(
  226. (specifier2) => specifier2.type === "ImportSpecifier" && specifier2.imported.type === "Identifier" && specifier2.imported.name === refName || specifier2.type === "ImportNamespaceSpecifier" && specifier2.local.name === refName
  227. );
  228. if (!specifier)
  229. continue;
  230. const resolved = await resolveTSFileId(node.source.value, file.filePath);
  231. if (!resolved)
  232. continue;
  233. const exports = await resolveTSExports(await getTSFile(resolved));
  234. let type2 = exports;
  235. for (const name of specifier.type === "ImportSpecifier" ? refNames : restNames) {
  236. type2 = type2 == null ? void 0 : type2[name];
  237. }
  238. return type2;
  239. }
  240. if (node.type === "ExportNamedDeclaration" && node.declaration)
  241. node = node.declaration;
  242. if (isTSDeclaration(node)) {
  243. if (((_a = node.id) == null ? void 0 : _a.type) !== "Identifier")
  244. continue;
  245. if (node.id.name !== refName)
  246. continue;
  247. const resolved = await resolveTSReferencedType(
  248. { scope, type: node },
  249. stacks
  250. );
  251. if (!resolved)
  252. return;
  253. if (restNames.length === 0) {
  254. return resolved;
  255. } else {
  256. let exports = resolved;
  257. for (const name of restNames) {
  258. exports = exports[name];
  259. }
  260. return exports;
  261. }
  262. }
  263. }
  264. if (type.type === "TSTypeReference")
  265. return { scope, type };
  266. }
  267. function resolveTSScope(scope) {
  268. const isFile = "ast" in scope;
  269. const file = isFile ? scope : resolveTSScope(scope.scope).file;
  270. const body = isFile ? scope.ast : scope.type.body;
  271. return {
  272. isFile,
  273. file,
  274. body
  275. };
  276. }
  277. function resolveTSEntityName(node) {
  278. if (node.type === "Identifier")
  279. return [node];
  280. else {
  281. return [...resolveTSEntityName(node.left), node.right];
  282. }
  283. }
  284. var exportsSymbol = Symbol("exports");
  285. var tsFileExportsCache = /* @__PURE__ */ new Map();
  286. function isTSExports(val) {
  287. return !!val && typeof val === "object" && exportsSymbol in val;
  288. }
  289. async function resolveTSExports(scope) {
  290. var _a;
  291. if (tsFileExportsCache.has(scope))
  292. return tsFileExportsCache.get(scope);
  293. const exports = {
  294. [exportsSymbol]: true
  295. };
  296. tsFileExportsCache.set(scope, exports);
  297. const { body, file } = resolveTSScope(scope);
  298. for (const stmt of body) {
  299. if (stmt.type === "ExportDefaultDeclaration") {
  300. } else if (stmt.type === "ExportAllDeclaration") {
  301. const resolved = await resolveTSFileId(stmt.source.value, file.filePath);
  302. if (!resolved)
  303. continue;
  304. const sourceExports = await resolveTSExports(await getTSFile(resolved));
  305. Object.assign(exports, sourceExports);
  306. } else if (stmt.type === "ExportNamedDeclaration") {
  307. let sourceExports;
  308. if (stmt.source) {
  309. const resolved = await resolveTSFileId(stmt.source.value, file.filePath);
  310. if (!resolved)
  311. continue;
  312. sourceExports = await resolveTSExports(await getTSFile(resolved));
  313. }
  314. for (const specifier of stmt.specifiers) {
  315. if (specifier.type === "ExportDefaultSpecifier") {
  316. continue;
  317. }
  318. if (specifier.type === "ExportNamespaceSpecifier") {
  319. exports[specifier.exported.name] = sourceExports;
  320. } else {
  321. const exportedName = specifier.exported.type === "Identifier" ? specifier.exported.name : specifier.exported.value;
  322. if (stmt.source) {
  323. exports[exportedName] = sourceExports[specifier.local.name];
  324. } else {
  325. exports[exportedName] = await resolveTSReferencedType({
  326. scope,
  327. type: specifier.local
  328. });
  329. }
  330. }
  331. }
  332. if (isTSDeclaration(stmt.declaration)) {
  333. const decl = stmt.declaration;
  334. if (((_a = decl.id) == null ? void 0 : _a.type) === "Identifier") {
  335. const exportedName = decl.id.name;
  336. exports[exportedName] = await resolveTSReferencedType({
  337. scope,
  338. type: decl
  339. });
  340. }
  341. }
  342. }
  343. }
  344. return exports;
  345. }
  346. var resolveTSFileIdImpl = resolveTSFileIdNode;
  347. function resolveTSFileId(id, importer) {
  348. return resolveTSFileIdImpl(id, importer);
  349. }
  350. function resolveTSFileIdNode(id, importer) {
  351. function tryResolve(id2, importer2) {
  352. const filePath = path.resolve(importer2, "..", id2);
  353. if (!existsSync(filePath))
  354. return;
  355. return filePath;
  356. }
  357. return tryResolve(id, importer) || tryResolve(`${id}.ts`, importer) || tryResolve(`${id}.d.ts`, importer) || tryResolve(`${id}/index`, importer) || tryResolve(`${id}/index.ts`, importer) || tryResolve(`${id}/index.d.ts`, importer);
  358. }
  359. function setResolveTSFileIdImpl(impl) {
  360. resolveTSFileIdImpl = impl;
  361. }
  362. export {
  363. tsFileCache,
  364. getTSFile,
  365. isTSDeclaration,
  366. mergeTSProperties,
  367. resolveTSProperties,
  368. resolveTypeElements,
  369. resolveTSReferencedType,
  370. resolveTSScope,
  371. resolveTSEntityName,
  372. exportsSymbol,
  373. tsFileExportsCache,
  374. isTSExports,
  375. resolveTSExports,
  376. resolveTSFileId,
  377. resolveTSFileIdNode,
  378. setResolveTSFileIdImpl
  379. };