diff --git a/package.json b/package.json index d2a5813..1a4944b 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "local-pkg": "^0.4.1", "mlly": "^0.5.3", "tinypool": "^0.2.1", "tsup": "^6.1.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46d6229..93e332b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,7 @@ importers: bumpp: ^8.2.1 eslint: ^8.18.0 esno: ^0.16.3 + local-pkg: ^0.4.1 mlly: ^0.5.3 pnpm: ^7.2.1 react: ^18.2.0 @@ -23,6 +24,7 @@ importers: vitest: ^0.15.1 vue: ^3.2.37 dependencies: + local-pkg: 0.4.1 mlly: 0.5.3 tinypool: 0.2.1 tsup: 6.1.2_typescript@4.7.4 @@ -2090,7 +2092,6 @@ packages: /local-pkg/0.4.1: resolution: {integrity: sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==} engines: {node: '>=14'} - dev: true /locate-path/2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} diff --git a/src/index.ts b/src/index.ts index 5682e36..0dac97f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,3 @@ -import Pool from 'tinypool' -import type { GetExportsOptions } from './types' - -let _worker: Pool | undefined - -export async function getExports(name: string, options?: GetExportsOptions) { - if (!_worker) { - _worker = new Pool({ - filename: new URL('./worker.js', import.meta.url).href, - }) - } - return await _worker.run({ name, options }) -} +export * from './runtime' +export * from './static' +export * from './types' diff --git a/src/runtime.ts b/src/runtime.ts new file mode 100644 index 0000000..d06325d --- /dev/null +++ b/src/runtime.ts @@ -0,0 +1,13 @@ +import Pool from 'tinypool' +import type { GetExportsOptions } from './types' + +let _worker: Pool | undefined + +export async function getExportsRuntime(name: string, options?: GetExportsOptions) { + if (!_worker) { + _worker = new Pool({ + filename: new URL('./worker.js', import.meta.url).href, + }) + } + return await _worker.run({ name, options }) +} diff --git a/src/static.ts b/src/static.ts new file mode 100644 index 0000000..5fce460 --- /dev/null +++ b/src/static.ts @@ -0,0 +1,54 @@ +import { promises as fs } from 'fs' +import { fileURLToPath, pathToFileURL } from 'url' +import { resolve } from 'path' +import { findExports, resolve as resolvePackagePath } from 'mlly' +import { getPackageInfo } from 'local-pkg' +import type { GetExportsOptions } from './types' + +export async function getExportsStatic(name: string, options?: GetExportsOptions) { + const map = new Map>() + + async function resolvePackageEntry(name: string, importer?: string) { + try { + const { rootPath, packageJson } = (await getPackageInfo(name, { paths: importer ? [fileURLToPath(importer)] : undefined }))! + if (!packageJson.exports && packageJson.module) + return pathToFileURL(resolve(rootPath, packageJson.module)).href + } + catch {} + + return await resolvePackagePath(name, { + url: importer, + extensions: ['.mjs', '.js'], + conditions: ['module', 'import', 'require', 'default'], + }) + } + + async function getExportsRelative(relative: string, importer?: string): Promise { + const url = relative.match(/^[@a-z]/) + ? await resolvePackageEntry(relative, importer) + : new URL(relative, importer).href + + return getExportsUrl(url) + } + + async function getExportsUrl(url: string) { + if (!map.has(url)) + map.set(url, _getExportsUrl(url)) + return await map.get(url)! + } + + async function _getExportsUrl(url: string) { + const code = await fs.readFile(fileURLToPath(url), 'utf8') + const exports = findExports(code) + + return (await Promise.all( + exports.map(async (i) => { + if (i.type === 'star' && i.specifier) + return await getExportsRelative(i.specifier, url) + return i.names + }))) + .flat() + } + + return getExportsRelative(name, options?.url) +} diff --git a/test/index.test.ts b/test/runtime.test.ts similarity index 79% rename from test/index.test.ts rename to test/runtime.test.ts index d99cfb0..807e620 100644 --- a/test/index.test.ts +++ b/test/runtime.test.ts @@ -1,10 +1,10 @@ import { resolve as resolvePackagePath } from 'mlly' import { describe, expect, it } from 'vitest' -import { getExports } from '../dist' +import { getExportsRuntime } from '../dist' describe('ESM', () => { it('@antfu/utils', async () => { - expect((await getExports('@antfu/utils')).slice(0, 5)) + expect((await getExportsRuntime('@antfu/utils')).slice(0, 5)) .toMatchInlineSnapshot(` [ "assert", @@ -17,7 +17,7 @@ describe('ESM', () => { }) it('@vue/shared', async () => { - expect((await getExports('@vue/shared', { + expect((await getExportsRuntime('@vue/shared', { url: await resolvePackagePath('vue'), })).slice(6, 12)) .toMatchInlineSnapshot(` @@ -35,7 +35,7 @@ describe('ESM', () => { describe('CJS', () => { it('axios', async () => { - expect((await getExports('axios')).slice(0, 5)) + expect((await getExportsRuntime('axios')).slice(0, 5)) .toMatchInlineSnapshot(` [ "request", @@ -48,7 +48,7 @@ describe('CJS', () => { }) it('react', async () => { - expect((await getExports('react')).slice(0, 5)) + expect((await getExportsRuntime('react')).slice(0, 5)) .toMatchInlineSnapshot(` [ "Children", diff --git a/test/static.test.ts b/test/static.test.ts new file mode 100644 index 0000000..3d2429d --- /dev/null +++ b/test/static.test.ts @@ -0,0 +1,137 @@ +import { describe, expect, it } from 'vitest' +import { getExportsStatic } from '../src' + +describe('ESM', () => { + it('@antfu/utils', async () => { + expect((await getExportsStatic('@antfu/utils')).slice(0, 5)) + .toMatchInlineSnapshot(` + [ + "assert", + "at", + "batchInvoke", + "clamp", + "clampArrayRange", + ] + `) + }) + + it.only('vue', async () => { + expect((await getExportsStatic('vue'))) + .toMatchInlineSnapshot(` + [ + "compile", + "Transition", + "TransitionGroup", + "VueElement", + "createApp", + "createSSRApp", + "defineCustomElement", + "defineSSRCustomElement", + "hydrate", + "initDirectivesForSSR", + "render", + "useCssModule", + "useCssVars", + "vModelCheckbox", + "vModelDynamic", + "vModelRadio", + "vModelSelect", + "vModelText", + "vShow", + "withKeys", + "withModifiers", + "BaseTransition", + "Comment", + "Fragment", + "KeepAlive", + "Static", + "Suspense", + "Teleport", + "Text", + "callWithAsyncErrorHandling", + "callWithErrorHandling", + "cloneVNode", + "compatUtils", + "computed", + "createBlock", + "createCommentVNode", + "createElementBlock", + "createElementVNode", + "createHydrationRenderer", + "createPropsRestProxy", + "createRenderer", + "createSlots", + "createStaticVNode", + "createTextVNode", + "createVNode", + "defineAsyncComponent", + "defineComponent", + "defineEmits", + "defineExpose", + "defineProps", + "devtools", + "getCurrentInstance", + "getTransitionRawChildren", + "guardReactiveProps", + "h", + "handleError", + "initCustomFormatter", + "inject", + "isMemoSame", + "isRuntimeOnly", + "isVNode", + "mergeDefaults", + "mergeProps", + "nextTick", + "onActivated", + "onBeforeMount", + "onBeforeUnmount", + "onBeforeUpdate", + "onDeactivated", + "onErrorCaptured", + "onMounted", + "onRenderTracked", + "onRenderTriggered", + "onServerPrefetch", + "onUnmounted", + "onUpdated", + "openBlock", + "popScopeId", + "provide", + "pushScopeId", + "queuePostFlushCb", + "registerRuntimeCompiler", + "renderList", + "renderSlot", + "resolveComponent", + "resolveDirective", + "resolveDynamicComponent", + "resolveFilter", + "resolveTransitionHooks", + "setBlockTracking", + "setDevtoolsHook", + "setTransitionHooks", + "ssrContextKey", + "ssrUtils", + "toHandlers", + "transformVNodeArgs", + "useAttrs", + "useSSRContext", + "useSlots", + "useTransitionState", + "version", + "warn", + "watch", + "watchEffect", + "watchPostEffect", + "watchSyncEffect", + "withAsyncContext", + "withCtx", + "withDefaults", + "withDirectives", + "withMemo", + "withScopeId", + ] + `) + }) +})