Skip to content

Commit 551e7c7

Browse files
authoredMay 16, 2023
fix!: parse existing dts file, remove cache (#367)
1 parent 9071c66 commit 551e7c7

File tree

7 files changed

+72
-91
lines changed

7 files changed

+72
-91
lines changed
 

‎README.md

-5
Original file line numberDiff line numberDiff line change
@@ -250,11 +250,6 @@ AutoImport({
250250
// Set `false` to disable.
251251
dts: './auto-imports.d.ts',
252252

253-
// Cache the result of resolving, across multiple vite builds.
254-
// A custom path is supported.
255-
// When set to `true`, the cache will be stored in `node_modules/.cache/unplugin-auto-import.json`.
256-
cache: false,
257-
258253
// Auto import inside Vue template
259254
// see https://github.com/unjs/unimport/pull/15 and https://github.com/unjs/unimport/pull/72
260255
vueTemplate: false,

‎src/core/ctx.ts

+39-69
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { dirname, isAbsolute, posix, relative, resolve, sep } from 'node:path'
2-
import { promises as fs } from 'node:fs'
1+
import { dirname, isAbsolute, relative, resolve } from 'node:path'
2+
import { existsSync, promises as fs } from 'node:fs'
33
import { slash, throttle, toArray } from '@antfu/utils'
44
import { createFilter } from '@rollup/pluginutils'
55
import { isPackageExists } from 'local-pkg'
@@ -9,14 +9,13 @@ import { createUnimport, resolvePreset, scanDirExports } from 'unimport'
99
import { vueTemplateAddon } from 'unimport/addons'
1010
import MagicString from 'magic-string'
1111
import { presets } from '../presets'
12-
import type { ESLintrc, ImportExtended, Options } from '../types'
12+
import type { ESLintGlobalsPropValue, ESLintrc, ImportExtended, Options } from '../types'
1313
import { generateESLintConfigs } from './eslintrc'
1414
import { resolversAddon } from './resolvers'
1515

1616
export function createContext(options: Options = {}, root = process.cwd()) {
1717
const {
1818
dts: preferDTS = isPackageExists('typescript'),
19-
cache: isCache = false,
2019
} = options
2120

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

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

31-
const cachePath = isCache === false
32-
? false
33-
: resolve(root, typeof isCache === 'string' ? isCache : 'node_modules/.cache/unplugin-auto-import.json')
34-
3530
// When "options.injectAtEnd" is undefined or true, it's true.
3631
const injectAtEnd = options.injectAtEnd !== false
3732

@@ -79,10 +74,27 @@ ${dts}`.trim()}\n`
7974
? resolve(root, 'auto-imports.d.ts')
8075
: resolve(root, preferDTS)
8176

77+
const multilineCommentsRE = /\/\*.*?\*\//gms
78+
const singlelineCommentsRE = /\/\/.*$/gm
79+
const dtsReg = /declare\s+global\s*{(.*?)}/s
80+
function parseDTS(dts: string) {
81+
dts = dts
82+
.replace(multilineCommentsRE, '')
83+
.replace(singlelineCommentsRE, '')
84+
85+
const code = dts.match(dtsReg)?.[0]
86+
if (!code)
87+
return
88+
89+
return Object.fromEntries(Array.from(code.matchAll(/['"]?(const\s*[^\s'"]+)['"]?\s*:\s*(.+?)[,;\r\n]/g)).map(i => [i[1], i[2]]))
90+
}
91+
8292
async function generateDTS(file: string) {
8393
await importsPromise
8494
const dir = dirname(file)
85-
return unimport.generateTypeDeclarations({
95+
const originalContent = existsSync(file) ? await fs.readFile(file, 'utf-8') : ''
96+
const originalDTS = parseDTS(originalContent)
97+
const currentContent = await unimport.generateTypeDeclarations({
8698
resolvePath: (i) => {
8799
if (i.from.startsWith('.') || isAbsolute(i.from)) {
88100
const related = slash(relative(dir, i.from).replace(/\.ts(x)?$/, ''))
@@ -93,14 +105,29 @@ ${dts}`.trim()}\n`
93105
return i.from
94106
},
95107
})
108+
const currentDTS = parseDTS(currentContent)!
109+
if (originalDTS) {
110+
Object.keys(currentDTS).forEach((key) => {
111+
originalDTS[key] = currentDTS[key]
112+
})
113+
const dtsList = Object.keys(originalDTS).sort().map(k => ` ${k}: ${originalDTS[k]}`)
114+
return currentContent.replace(dtsReg, `declare global {\n${dtsList.join('\n')}\n}`)
115+
}
116+
117+
return currentContent
118+
}
119+
120+
async function parseESLint() {
121+
const configStr = existsSync(eslintrc.filepath!) ? await fs.readFile(eslintrc.filepath!, 'utf-8') : ''
122+
const config = JSON.parse(configStr || '{ "globals": {} }')
123+
return config.globals as Record<string, ESLintGlobalsPropValue>
96124
}
97125

98126
async function generateESLint() {
99-
return generateESLintConfigs(await unimport.getImports(), eslintrc)
127+
return generateESLintConfigs(await unimport.getImports(), eslintrc, await parseESLint())
100128
}
101129

102130
const writeConfigFilesThrottled = throttle(500, writeConfigFiles, { noLeading: false })
103-
const writeFileThrottled = throttle(500, writeFile, { noLeading: false })
104131

105132
async function writeFile(filePath: string, content = '') {
106133
await fs.mkdir(dirname(filePath), { recursive: true })
@@ -150,68 +177,12 @@ ${dts}`.trim()}\n`
150177
writeConfigFilesThrottled()
151178
}
152179

153-
async function getCacheData(cache: string) {
154-
const str = (await fs.readFile(cache, 'utf-8')).trim()
155-
return JSON.parse(str || '{}') as { [key: string]: Import[] }
156-
}
157-
158-
async function generateCache(): Promise<Record<string, Import[]>> {
159-
if (!cachePath)
160-
return {}
161-
162-
let cacheData = {}
163-
try {
164-
cacheData = await getCacheData(cachePath)
165-
await Promise.allSettled(Object.keys(cacheData).map(async (filePath) => {
166-
try {
167-
const normalizeRoot = root.replaceAll(sep, posix.sep)
168-
await fs.access(posix.join(normalizeRoot, filePath))
169-
}
170-
catch {
171-
Reflect.deleteProperty(cacheData, filePath)
172-
}
173-
}))
174-
await writeFile(cachePath, JSON.stringify(cacheData, null, 2))
175-
}
176-
catch {
177-
await writeFile(cachePath, '{}')
178-
}
179-
180-
return cacheData
181-
}
182-
183-
let isInitialCache = false
184-
const resolveCachePromise = generateCache()
185-
async function updateCacheImports(id?: string, importList?: Import[]) {
186-
if (!cachePath || (isInitialCache && !id))
187-
return
188-
189-
isInitialCache = true
190-
const cacheData = await resolveCachePromise
191-
await unimport.modifyDynamicImports(async (imports) => {
192-
if (id && importList) {
193-
const filePath = posix.normalize(posix.relative(root, id))
194-
importList = importList.filter(i => (i.name ?? i.as) && i.name !== 'default')
195-
if (importList.length)
196-
cacheData[filePath] = importList
197-
else
198-
delete cacheData[filePath]
199-
writeFileThrottled(cachePath, JSON.stringify(cacheData, null, 2))
200-
return imports.concat(importList)
201-
}
202-
203-
return imports.concat(Object.values(cacheData).reduce((p, n) => p.concat(n), []))
204-
})
205-
}
206-
207180
async function transform(code: string, id: string) {
208181
await importsPromise
209182

210183
const s = new MagicString(code)
211184

212-
const res = await unimport.injectImports(s, id)
213-
214-
await updateCacheImports(id, res.imports)
185+
await unimport.injectImports(s, id)
215186

216187
if (!s.hasChanged())
217188
return
@@ -229,7 +200,6 @@ ${dts}`.trim()}\n`
229200
dirs,
230201
filter,
231202
scanDirs,
232-
updateCacheImports,
233203
writeConfigFiles,
234204
writeConfigFilesThrottled,
235205
transform,

‎src/core/eslintrc.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import type { Import } from 'unimport'
22
import type { ESLintGlobalsPropValue, ESLintrc } from '../types'
33

4-
interface ESLintConfigs {
5-
globals: Record<string, ESLintGlobalsPropValue>
6-
}
7-
84
export function generateESLintConfigs(
95
imports: Import[],
106
eslintrc: ESLintrc,
7+
globals: Record<string, ESLintGlobalsPropValue> = {},
118
) {
12-
const eslintConfigs: ESLintConfigs = { globals: {} }
9+
const eslintConfigs = { globals }
1310

1411
imports
1512
.map(i => i.as ?? i.name)

‎src/core/unplugin.ts

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ export default createUnplugin<Options>((options) => {
1717
},
1818
async buildStart() {
1919
await ctx.scanDirs()
20-
await ctx.updateCacheImports()
21-
await ctx.writeConfigFiles()
2220
},
2321
async buildEnd() {
2422
await ctx.writeConfigFiles()

‎src/types.ts

-10
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,6 @@ export interface Options {
111111
*/
112112
dts?: string | boolean
113113

114-
/**
115-
* Cache the result of resolving, across multiple vite builds.
116-
*
117-
* A custom path is supported.
118-
* When set to `true`, the cache will be stored in `node_modules/.cache/unplugin-auto-import.json`.
119-
*
120-
* @default false
121-
*/
122-
cache?: string | boolean
123-
124114
/**
125115
* Auto import inside Vue templates
126116
*

‎test/dts.increase.test.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { join } from 'node:path'
2+
import { expect, it } from 'vitest'
3+
import { createContext } from '../src/core/ctx'
4+
5+
it('dts', async () => {
6+
const cwd = process.cwd()
7+
const dts = join(cwd, './test/tmp/dts.increase.d.ts')
8+
const ctx = createContext({
9+
ignore: ['h'],
10+
imports: ['vue'],
11+
dts,
12+
})
13+
14+
const dtsContent = await ctx.generateDTS(dts)
15+
expect(dtsContent).toContain('AAA')
16+
expect(dtsContent).toContain('BBB')
17+
})

‎test/tmp/dts.increase.d.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* eslint-disable */
2+
/* prettier-ignore */
3+
// @ts-nocheck
4+
// Generated by unplugin-auto-import
5+
export {}
6+
declare global {
7+
const AAA: typeof import('xxx/es')['AAA']
8+
const BBB: typeof import('xxx/es')['BBB']
9+
}
10+
// for type re-export
11+
declare global {
12+
// @ts-ignore
13+
export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue'
14+
}

0 commit comments

Comments
 (0)
Please sign in to comment.