Skip to content

Commit

Permalink
feat(cache): cache dynamic imports for generate dts,eslint (#311)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <[email protected]>
  • Loading branch information
lishaobos and antfu authored Feb 16, 2023
1 parent 5471479 commit ee65fc6
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 4 deletions.
1 change: 1 addition & 0 deletions playground/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default defineConfig({
'./composables',
],
vueTemplate: true,
cache: true,
}),
],
})
66 changes: 63 additions & 3 deletions src/core/ctx.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { dirname, isAbsolute, relative, resolve } from 'path'
import { dirname, isAbsolute, posix, relative, resolve } from 'path'
import { promises as fs } from 'fs'
import { slash, throttle, toArray } from '@antfu/utils'
import { createFilter } from '@rollup/pluginutils'
Expand All @@ -16,6 +16,7 @@ import { resolversAddon } from './resolvers'
export function createContext(options: Options = {}, root = process.cwd()) {
const {
dts: preferDTS = isPackageExists('typescript'),
cache: isCache = false,
} = options

const dirs = options.dirs?.map(dir => resolve(root, dir))
Expand All @@ -27,6 +28,10 @@ export function createContext(options: Options = {}, root = process.cwd()) {

const resolvers = options.resolvers ? [options.resolvers].flat(2) : []

const cachePath = isCache === false
? false
: resolve(root, typeof isCache === 'string' ? 'string' : 'node_modules/.cache/unplugin-auto-import.json')

const unimport = createUnimport({
imports: [],
presets: [],
Expand Down Expand Up @@ -90,7 +95,7 @@ export function createContext(options: Options = {}, root = process.cwd()) {

const writeConfigFilesThrottled = throttle(500, writeConfigFiles, { noLeading: false })

async function writeFile(filePath: string, content: string) {
async function writeFile(filePath: string, content = '') {
await fs.mkdir(dirname(filePath), { recursive: true })
return await fs.writeFile(filePath, content, 'utf-8')
}
Expand Down Expand Up @@ -138,16 +143,70 @@ export function createContext(options: Options = {}, root = process.cwd()) {
writeConfigFilesThrottled()
}

async function getCacheData(cache: string) {
const str = (await fs.readFile(cache, 'utf-8')).trim()
return JSON.parse(str || '{}') as { [key: string]: Import[] }
}

async function generateCache() {
if (!cachePath)
return

try {
const cacheData = await getCacheData(cachePath)
await Promise.allSettled(Object.keys(cacheData).map(async (filePath) => {
try {
await fs.access(posix.resolve(root, filePath))
}
catch {
Reflect.deleteProperty(cacheData, filePath)
}
}))
await writeFile(cachePath, JSON.stringify(cacheData, null, 2))
}
catch {
await writeFile(cachePath, '{}')
}
}

let isInitialCache = false
const resolveCachePromise = generateCache()
async function updateCacheImports(id?: string, importList?: Import[]) {
if (!cachePath || (isInitialCache && !id))
return

isInitialCache = true
await resolveCachePromise
await unimport.modifyDynamicImports(async (imports) => {
const cacheData = await getCacheData(cachePath)

if (id && importList) {
const filePath = posix.normalize(relative(root, id))
importList = importList.filter(i => (i.name ?? i.as) && i.name !== 'default')
if (importList.length)
cacheData[filePath] = importList
else
delete cacheData[filePath]
await writeFile(cachePath, JSON.stringify(cacheData, null, 2))
return imports.concat(importList)
}

return imports.concat(Object.values(cacheData).reduce((p, n) => p.concat(n), []))
})
}

async function transform(code: string, id: string) {
await importsPromise

const s = new MagicString(code)

await unimport.injectImports(s, id)
const res = await unimport.injectImports(s, id)

if (!s.hasChanged())
return

await updateCacheImports(id, res.imports)

writeConfigFilesThrottled()

return {
Expand All @@ -161,6 +220,7 @@ export function createContext(options: Options = {}, root = process.cwd()) {
dirs,
filter,
scanDirs,
updateCacheImports,
writeConfigFiles,
writeConfigFilesThrottled,
transform,
Expand Down
5 changes: 4 additions & 1 deletion src/core/unplugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ export default createUnplugin<Options>((options) => {
return ctx.transform(code, id)
},
async buildStart() {
await ctx.scanDirs()
await Promise.all([
ctx.scanDirs(),
ctx.updateCacheImports(),
])
},
async buildEnd() {
await ctx.writeConfigFiles()
Expand Down
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ export interface Options {
*/
dts?: string | boolean

/**
* Cache the result of resolving, across multiple vite builds.
*
* A custom path is supported.
* When set to `true`, the cache will be stored in `node_modules/.cache/unplugin-auto-import.json`.
*
* @default false
*/
cache?: string | boolean

/**
* Auto import inside Vue templates
*
Expand Down

0 comments on commit ee65fc6

Please sign in to comment.