diff --git a/src/core/ctx.ts b/src/core/ctx.ts index ff54244..168df16 100644 --- a/src/core/ctx.ts +++ b/src/core/ctx.ts @@ -1,10 +1,11 @@ -import { dirname, isAbsolute, relative, resolve } from 'node:path' +import { dirname, isAbsolute, join, relative, resolve } from 'node:path' import { existsSync, promises as fs } from 'node:fs' import { slash, throttle, toArray } from '@antfu/utils' import { createFilter } from '@rollup/pluginutils' import { isPackageExists } from 'local-pkg' import type { Import, InlinePreset } from 'unimport' -import { createUnimport, resolvePreset, scanDirExports } from 'unimport' +import { createUnimport, resolvePreset, scanExports } from 'unimport' +import fg from 'fast-glob' // @ts-expect-error types import { vueTemplateAddon } from 'unimport/addons' @@ -14,12 +15,30 @@ import type { ESLintGlobalsPropValue, ESLintrc, ImportExtended, Options } from ' import { generateESLintConfigs } from './eslintrc' import { resolversAddon } from './resolvers' +function resolveGlobsExclude(root: string, glob: string) { + const excludeReg = /^!/ + return `${excludeReg.test(glob) ? '!' : ''}${resolve(root, glob.replace(excludeReg, ''))}` +} + +async function scanDirExports(dirs: string[], root: string) { + const result = await fg(dirs, { + absolute: true, + cwd: root, + onlyFiles: true, + followSymbolicLinks: true, + }) + + const files = Array.from(new Set(result.flat())).map(slash) + return (await Promise.all(files.map(i => scanExports(i)))).flat() +} + export function createContext(options: Options = {}, root = process.cwd()) { const { dts: preferDTS = isPackageExists('typescript'), } = options - const dirs = options.dirs?.map(dir => resolve(root, dir)) + const dirs = options.dirs?.concat(options.dirs.map(dir => join(dir, '*.{tsx,jsx,ts,js,mjs,cjs,mts,cts}'))) + .map(dir => slash(resolveGlobsExclude(root, dir))) const eslintrc: ESLintrc = options.eslintrc || {} eslintrc.enabled = eslintrc.enabled === undefined ? false : eslintrc.enabled @@ -167,9 +186,7 @@ ${dts}`.trim()}\n` async function scanDirs() { if (dirs?.length) { await unimport.modifyDynamicImports(async (imports) => { - const exports_ = await scanDirExports(dirs, { - filePatterns: ['*.{tsx,jsx,ts,js,mjs,cjs,mts,cts}'], - }) as ImportExtended[] + const exports_ = await scanDirExports(dirs, root) as ImportExtended[] exports_.forEach(i => i.__source = 'dir') return modifyDefaultExportsAlias([ ...imports.filter((i: ImportExtended) => i.__source !== 'dir'), diff --git a/test/search.test.ts b/test/search.test.ts new file mode 100644 index 0000000..8f836b4 --- /dev/null +++ b/test/search.test.ts @@ -0,0 +1,36 @@ +import { resolve } from 'node:path' +import { describe, it } from 'vitest' +import { createContext } from '../src/core/ctx' + +const root = resolve(__dirname, '../examples/vite-react') + +describe('search', () => { + it('should dir work', async () => { + const ctx = createContext({ + dts: false, + dirs: [ + 'src/views', + ], + }, root) + + await ctx.scanDirs() + const data = await ctx.generateDTS('') + expect(data).toContain('PageA') + expect(data).toContain('PageB') + }) + + it('should dir excude work', async () => { + const ctx = createContext({ + dts: false, + dirs: [ + 'src/**', + '!src/views', + ], + }, root) + + await ctx.scanDirs() + const data = await ctx.generateDTS('') + expect(data).not.toContain('PageA') + expect(data).not.toContain('PageB') + }) +})