Skip to content

Commit

Permalink
feat: support to import the types from the dirs (#544)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <github@antfu.me>
  • Loading branch information
noootwo and antfu authored Dec 13, 2024
1 parent 3cd7085 commit 902416f
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 53 deletions.
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,27 @@ AutoImport({
// Enable auto import by filename for default module exports under directories
defaultExportByFilename: false,

// Options for scanning directories for auto import
dirsScanOptions: {
types: true // Enable auto import the types under the directories
},

// Auto import for module exports under directories
// by default it only scan one level of modules under the directory
dirs: [
// './hooks',
// './composables' // only root modules
// './composables/**', // all nested modules
'./hooks',
'./composables', // only root modules
'./composables/**', // all nested modules
// ...

{
glob: './hooks',
types: true // enable import the types
},
{
glob: './composables',
types: false // If top level dirsScanOptions.types importing enabled, just only disable this directory
}
// ...
],

Expand Down
1 change: 1 addition & 0 deletions examples/vite-react/src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type SpecialType = string
2 changes: 2 additions & 0 deletions examples/vite-react/src/views/PageA.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ function PageA() {
)
}

export type TypeA = number

export default PageA
2 changes: 2 additions & 0 deletions examples/vite-react/src/views/PageB.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ function PageB() {
)
}

export type TypeB = number

export default PageB
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,9 @@
"dependencies": {
"@antfu/utils": "^0.7.10",
"@rollup/pluginutils": "^5.1.3",
"fast-glob": "^3.3.2",
"local-pkg": "^0.5.1",
"magic-string": "^0.30.15",
"minimatch": "^9.0.5",
"picomatch": "^4.0.2",
"unimport": "^3.14.5",
"unplugin": "^2.1.0"
},
Expand All @@ -178,11 +177,13 @@
"@nuxt/kit": "^3.14.1592",
"@svgr/plugin-jsx": "^8.1.0",
"@types/node": "^22.10.2",
"@types/picomatch": "^3.0.1",
"@types/resolve": "^1.20.6",
"@vueuse/metadata": "^12.0.0",
"bumpp": "^9.9.1",
"eslint": "^9.16.0",
"esno": "^4.8.0",
"fast-glob": "^3.3.2",
"publint": "^0.2.12",
"rollup": "^4.28.1",
"tsup": "^8.3.5",
Expand Down
20 changes: 14 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 18 additions & 33 deletions src/core/ctx.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,29 @@
import type { Import, InlinePreset } from 'unimport'
import type { BiomeLintrc, ESLintGlobalsPropValue, ESLintrc, ImportExtended, Options } from '../types'
import { existsSync, promises as fs } from 'node:fs'
import { dirname, isAbsolute, join, relative, resolve } from 'node:path'
import { dirname, isAbsolute, relative, resolve } from 'node:path'
import process from 'node:process'
import { slash, throttle, toArray } from '@antfu/utils'
import { createFilter } from '@rollup/pluginutils'
import fg from 'fast-glob'
import { isPackageExists } from 'local-pkg'
import MagicString from 'magic-string'
import { createUnimport, resolvePreset, scanExports } from 'unimport'
import { createUnimport, resolvePreset } from 'unimport'
import { presets } from '../presets'
import { generateBiomeLintConfigs } from './biomelintrc'
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, false)))).flat()
}

export function createContext(options: Options = {}, root = process.cwd()) {
root = slash(root)

const {
dts: preferDTS = isPackageExists('typescript'),
dirsScanOptions,
dirs,
vueDirectives,
vueTemplate,
} = options

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
eslintrc.filepath = eslintrc.filepath || './.eslintrc-auto-import.json'
Expand All @@ -64,6 +45,11 @@ export function createContext(options: Options = {}, root = process.cwd()) {
const unimport = createUnimport({
imports: [],
presets: options.packagePresets?.map(p => typeof p === 'string' ? { package: p } : p) ?? [],
dirsScanOptions: {
...dirsScanOptions,
cwd: root,
},
dirs,
injectAtEnd,
parser: options.parser,
addons: {
Expand Down Expand Up @@ -266,16 +252,15 @@ ${dts}`.trim()}\n`
}

async function scanDirs() {
if (dirs?.length) {
await unimport.modifyDynamicImports(async (imports) => {
const exports_ = await scanDirExports(dirs, root) as ImportExtended[]
exports_.forEach(i => i.__source = 'dir')
return modifyDefaultExportsAlias([
...imports.filter((i: ImportExtended) => i.__source !== 'dir'),
...exports_,
], options)
})
}
await unimport.modifyDynamicImports(async (imports) => {
const exports_ = await unimport.scanImportsFromDir() as ImportExtended[]
exports_.forEach(i => i.__source = 'dir')
return modifyDefaultExportsAlias([
...imports.filter((i: ImportExtended) => i.__source !== 'dir'),
...exports_,
], options)
})

writeConfigFilesThrottled()
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/unplugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Options } from '../types'
import { slash } from '@antfu/utils'
import { isPackageExists } from 'local-pkg'
import { minimatch } from 'minimatch'
import pm from 'picomatch'
import { createUnplugin } from 'unplugin'
import { createContext } from './ctx'

Expand Down Expand Up @@ -41,7 +41,7 @@ export default createUnplugin<Options>((options) => {
}
},
async handleHotUpdate({ file }) {
if (ctx.dirs?.some(glob => minimatch(slash(file), slash(glob))))
if (ctx.dirs?.some(dir => pm.isMatch(slash(file), slash(typeof dir === 'string' ? dir : dir.glob))))
await ctx.scanDirs()
},
async configResolved(config) {
Expand Down
26 changes: 25 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,25 @@ export type Resolver = ResolverFunction | ResolverResultObject
*/
export type ImportsMap = Record<string, (string | ImportNameAlias)[]>

export interface ScanDirExportsOptions {
/**
* Register type exports
*
* @default true
*/
types?: boolean
}

/**
* Directory to search for import
*/
export interface ScanDir {
glob: string
types?: boolean
}

export type NormalizedScanDir = Required<ScanDir>

export type ESLintGlobalsPropValue = boolean | 'readonly' | 'readable' | 'writable' | 'writeable'

export interface ESLintrc {
Expand Down Expand Up @@ -118,10 +137,15 @@ export interface Options {
*/
injectAtEnd?: boolean

/**
* Options for scanning directories for auto import
*/
dirsScanOptions?: ScanDirExportsOptions

/**
* Path for directories to be auto imported
*/
dirs?: string[]
dirs?: (string | ScanDir)[]

/**
* Pass a custom function to resolve the component importing path from the component name.
Expand Down
59 changes: 57 additions & 2 deletions test/search.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { resolve } from 'node:path'
import { describe, it } from 'vitest'
import { describe, expect, it } from 'vitest'
import { createContext } from '../src/core/ctx'

const root = resolve(__dirname, '../examples/vite-react')
Expand All @@ -19,7 +19,7 @@ describe('search', () => {
expect(data).toContain('PageB')
})

it('should dir excude work', async () => {
it('should dir exclude work', async () => {
const ctx = createContext({
dts: false,
dirs: [
Expand All @@ -34,3 +34,58 @@ describe('search', () => {
expect(data).not.toContain('PageB')
})
})

describe('import the types from the dirs', () => {
it('should top level types enable work', async () => {
const ctx = createContext({
dts: false,
dirsScanOptions: { types: true },
dirs: ['src/**'],
}, root)

await ctx.scanDirs()
const data = await ctx.generateDTS('')
expect(data).toContain('TypeA')
expect(data).toContain('TypeB')
expect(data).toContain('SpecialType')
})

it('should specific dirs types enable work', async () => {
const ctx = createContext({
dts: false,
dirsScanOptions: { types: true },
dirs: [
{
glob: 'src/views',
types: true,
},
],
}, root)

await ctx.scanDirs()
const data = await ctx.generateDTS('')
expect(data).toContain('TypeA')
expect(data).toContain('TypeB')
expect(data).not.toContain('SpecialType')
})

it('should specific dirs types disable work', async () => {
const ctx = createContext({
dts: false,
dirsScanOptions: { types: true },
dirs: [
'src/types',
{
glob: 'src/views',
types: false,
},
],
}, root)

await ctx.scanDirs()
const data = await ctx.generateDTS('')
expect(data).not.toContain('TypeA')
expect(data).not.toContain('TypeB')
expect(data).toContain('SpecialType')
})
})
6 changes: 3 additions & 3 deletions test/transform.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { promises as fs } from 'node:fs'
import { resolve } from 'node:path'
import fg from 'fast-glob'
import glob from 'fast-glob'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { describe, expect, it } from 'vitest'
import { createContext } from '../src/core/ctx'
Expand Down Expand Up @@ -61,7 +61,7 @@ describe('transform', async () => {
})

const root = resolve(__dirname, 'fixtures')
const files = await fg('*', {
const files = await glob('*', {
cwd: root,
onlyFiles: true,
})
Expand All @@ -85,7 +85,7 @@ describe('transform-vue-macro', async () => {
})

const root = resolve(__dirname, 'fixtures-vue-macro')
const files = await fg('*', {
const files = await glob('*', {
cwd: root,
onlyFiles: true,
})
Expand Down
2 changes: 1 addition & 1 deletion vitest.config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineConfig } from 'vite'
import { defineConfig } from 'vitest/config'
import AutoImport from './src/vite'

export default defineConfig({
Expand Down

0 comments on commit 902416f

Please sign in to comment.