UNPKG

common-intellisense

Version:
331 lines (305 loc) 11 kB
import { existsSync } from 'node:fs' import fsp from 'node:fs/promises' import path from 'node:path' import { createFakeProgress, getConfiguration, getLocale, getRootPath, message } from '@vscode-use/utils' import { ofetch } from 'ofetch' import { latestVersion } from '@simon_he/latest-version' import { componentsReducer, propsReducer } from './ui/utils' import { logger } from './ui-find' const prefix = '@common-intellisense/' export const cacheFetch = new Map() export const localCacheUri = path.resolve(__dirname, 'mapping.json') let isCommonIntellisenseInProgress = false let isRemoteUrisInProgress = false let isLocalUrisInProgress = false const retry = 1 const timeout = 600000 // 如果 10 分钟拿不到就认为是 proxy 问题 const isZh = getLocale()?.includes('zh') export const getLocalCache = new Promise((resolve) => { if (existsSync(localCacheUri)) { fsp.readFile(localCacheUri, 'utf-8').then((res) => { logger.info(isZh ? `正在读取 ${localCacheUri} 中的数据` : `Reading data from ${localCacheUri}`) try { const oldMap = JSON.parse(res) as [string, string][] oldMap.forEach(([key, value]) => { cacheFetch.set(key, value) }) } catch (error) { logger.error(String(error)) } resolve('done reading') // 列出已有的 key const cacheKey = Array.from(cacheFetch.keys()).join(' | ') logger.info(isZh ? `缓存读取完毕, 已缓存的 key: ${cacheKey}` : `Cache read complete, cached keys: ${cacheKey}`) }) } else { resolve('done reading') } }) // todo: add result type replace any export async function fetchFromCommonIntellisense(tag: string) { const name = prefix + tag let version = '' logger.info(isZh ? `正在查找 ${name} 的最新版本...` : `Looking for the latest version of ${name}...`) try { version = await latestVersion(name, { cwd: getRootPath(), timeout: 5000, concurrency: 3 }) } catch (error: any) { if (error.message.includes('404 Not Found')) { // 说明这个版本还未支持, 可以通过 issue 提出 logger.error(isZh ? `当前版本并未支持` : `The current version is not supported`) } else { logger.error(String(error)) } return } logger.info(isZh ? `找到 ${name} 的最新版本: ${version}` : `Found the latest version of ${name}: ${version}`) const key = `${name}@${version}` // 当版本修改是否要删除相同 name 下的其它版本缓存? if (isCommonIntellisenseInProgress) return let resolver: () => void = () => { } let rejecter: (msg?: string) => void = () => { } isCommonIntellisenseInProgress = true if (!cacheFetch.has(key)) { createFakeProgress({ title: isZh ? `正在拉取远程的 ${tag}` : `Pulling remote ${tag}`, message: v => isZh ? `已完成 ${v}%` : `Completed ${v}%`, callback: (resolve, reject) => { resolver = resolve rejecter = reject }, }) } try { if (cacheFetch.has(key)) { logger.info(isZh ? `已缓存的 ${key}` : `cachedKey: ${key}`) } else { logger.info(isZh ? `准备拉取的资源: ${key}` : `ready fetchingKey: ${key}`) } const scriptContent = cacheFetch.has(key) ? cacheFetch.get(key) : await Promise.any([ ofetch(`https://cdn.jsdelivr.net/npm/${key}/dist/index.cjs`, { responseType: 'text', retry, timeout }), ofetch(`https://unpkg.com/${key}/dist/index.cjs`, { responseType: 'text', retry, timeout }), ofetch(`https://registry.npmmirror.com/${key}/dist/index.cjs`, { responseType: 'text' }), ofetch(`https://registry.npmjs.org/${key}/dist/index.cjs`, { responseType: 'text' }), ofetch(`https://r.cnpmjs.org/${key}/dist/index.cjs`, { responseType: 'text' }), ofetch(`https://cdn.jsdelivr.net/npm/${key}/dist/index.cjs`, { responseType: 'text' }), ]) cacheFetch.set(key, scriptContent) const module: any = {} const runModule = new Function('module', scriptContent) runModule(module) const moduleExports = module.exports const result: any = {} for (const key in moduleExports) { const v = moduleExports[key] if (key.endsWith('Components')) { result[key] = () => componentsReducer(v(isZh)) } else { result[key] = () => propsReducer(v()) } } resolver() isCommonIntellisenseInProgress = false return result } catch (error) { rejecter(String(error)) logger.error(String(error)) isCommonIntellisenseInProgress = false // 尝试从本地获取 message.error(isZh ? `从远程拉取 UI 包失败 ☹️,请检查代理` : `Failed to pull UI package from remote ☹️, please check the proxy`) return fetchFromLocalUris() // todo:增加重试机制 } } export async function fetchFromRemoteUrls() { // 读区 urls const uris = getConfiguration('common-intellisense.remoteUris') as string[] if (!uris.length) return const result: any = {} if (isRemoteUrisInProgress) return let resolver!: () => void let rejecter!: (msg?: string) => void isRemoteUrisInProgress = true createFakeProgress({ title: isZh ? `正在拉取远程文件` : 'Pulling remote files', message: v => isZh ? `已完成 ${v}%` : `Completed ${v}%`, callback(resolve, reject) { resolver = resolve rejecter = reject }, }) logger.info(isZh ? '从 remoteUris 中拉取数据...' : 'Fetching data from remoteUris...') try { const scriptContents = await Promise.all(uris.map(async (uri) => { logger.info(isZh ? `正在加载 ${uri}` : `Loading ${uri}`) return [uri, cacheFetch.has(uri) ? cacheFetch.get(uri) : await ofetch(uri, { responseType: 'text', retry, timeout })] })) scriptContents.forEach(([uri, scriptContent]) => { const module: any = {} const runModule = new Function('module', scriptContent) cacheFetch.set(uri, scriptContent) runModule(module) const moduleExports = module.exports const temp: any = {} const isZh = getLocale()!.includes('zh') for (const key in moduleExports) { const v = moduleExports[key] if (key.endsWith('Components')) { temp[key] = () => componentsReducer(v(isZh)) } else { temp[key] = () => propsReducer(v()) } } Object.assign(result, temp) }) resolver() } catch (error) { rejecter(String(error)) logger.error(String(error)) } isRemoteUrisInProgress = false return result } export async function fetchFromRemoteNpmUrls() { // 读区 urls const uris = getConfiguration('common-intellisense.remoteNpmUris') as string[] if (!uris.length) return const result: any = {} if (isRemoteUrisInProgress) return let resolver!: () => void let rejecter!: (msg?: string) => void isRemoteUrisInProgress = true createFakeProgress({ title: isZh ? `正在拉取远程 NPM 文件` : 'Pulling remote NPM files', message: v => isZh ? `已完成 ${v}%` : `Completed ${v}%`, callback(resolve, reject) { resolver = resolve rejecter = reject }, }) logger.info(isZh ? '从 remoteNpmUris 中拉取数据...' : 'Fetching data from remoteNpmUris...') try { const scriptContents = await Promise.all(uris.map(async (key) => { logger.info(isZh ? `正在加载 ${key}` : `Loading ${key}`) return [key, cacheFetch.has(key) ? cacheFetch.get(key) : await Promise.any([ ofetch(`https://cdn.jsdelivr.net/npm/${key}/dist/index.cjs`, { responseType: 'text', retry, timeout }), ofetch(`https://unpkg.com/${key}/dist/index.cjs`, { responseType: 'text', retry, timeout }), ofetch(`https://registry.npmmirror.com/${key}/dist/index.cjs`, { responseType: 'text' }), ofetch(`https://registry.npmjs.org/${key}/dist/index.cjs`, { responseType: 'text' }), ofetch(`https://r.cnpmjs.org/${key}/dist/index.cjs`, { responseType: 'text' }), ofetch(`https://cdn.jsdelivr.net/npm/${key}/dist/index.cjs`, { responseType: 'text' }), ])] })) scriptContents.forEach(([uri, scriptContent]) => { const module: any = {} const runModule = new Function('module', scriptContent) cacheFetch.set(uri, scriptContent) runModule(module) const moduleExports = module.exports const temp: any = {} const isZh = getLocale()!.includes('zh') for (const key in moduleExports) { const v = moduleExports[key] if (key.endsWith('Components')) { temp[key] = () => componentsReducer(v(isZh)) } else { temp[key] = () => propsReducer(v()) } } Object.assign(result, temp) }) resolver() } catch (error) { rejecter(String(error)) logger.error(String(error)) } isRemoteUrisInProgress = false return result } export async function fetchFromLocalUris() { let uris = getConfiguration('common-intellisense.localUris') as string[] if (!uris.length) return logger.info(`localUris: ${uris}`) // 查找本地文件 是否存在 uris = uris.map((uri) => { // 如果是相对路径,转换为绝对路径,否则直接用 if (uri.startsWith('./')) uri = path.resolve(getRootPath()!, uri) if (cacheFetch.has(uri) || existsSync(uri)) { return uri } else { logger.error(isZh ? `加载本地文件不存在: [${uri}]` : `Local file does not exist: [${uri}]`) return false } }).filter(Boolean) as string[] if (!uris.length) return const result: any = {} if (isLocalUrisInProgress) return let resolver!: () => void let rejecter!: (msg?: string) => void isLocalUrisInProgress = true createFakeProgress({ title: isZh ? `正在加载本地文件` : 'Loading local files', message: v => isZh ? `已完成 ${v}%` : `Completed ${v}%`, callback(resolve, reject) { resolver = resolve rejecter = reject }, }) try { await Promise.all(uris.map(async (uri) => { if (cacheFetch.has(uri)) { Object.assign(result, cacheFetch.get(uri)) return } const module: any = {} const scriptContent = await fsp.readFile(uri, 'utf-8') cacheFetch.set(uri, scriptContent) const runModule = new Function('module', scriptContent) runModule(module) const moduleExports = module.exports const temp: any = {} const isZh = getLocale()!.includes('zh') for (const key in moduleExports) { const v = moduleExports[key] if (key.endsWith('Components')) { temp[key] = () => componentsReducer(v(isZh)) } else { temp[key] = () => propsReducer(v()) } } Object.assign(result, temp) })) resolver() } catch (error) { rejecter(String(error)) logger.error(String(error)) } isLocalUrisInProgress = false return result }