Skip to content

Commit 09c0eac

Browse files
authored
refactor: migrate to ocache (#74)
* refactor: migrate to ocache * fix: catch error * fix: coderabbit
1 parent e8eb38d commit 09c0eac

22 files changed

Lines changed: 111 additions & 320 deletions

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@
228228
"module-replacements": "catalog:test",
229229
"msw": "catalog:test",
230230
"nano-staged": "catalog:dev",
231+
"ocache": "catalog:inline",
231232
"ofetch": "catalog:inline",
232233
"pathe": "catalog:inline",
233234
"perfect-debounce": "catalog:inline",

pnpm-lock.yaml

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ catalogs:
1515
inline:
1616
fast-npm-meta: ^1.3.0
1717
jsonc-parser: ^3.3.1
18+
ocache: ^0.1.2
1819
ofetch: ^2.0.0-alpha.3
1920
pathe: ^2.0.3
2021
perfect-debounce: ^2.1.0
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { MaybeError, PackageVersionsInfoWithMetadata } from 'fast-npm-meta'
2+
import { CACHE_MAX_AGE_ONE_DAY } from '#constants'
23
import { logger } from '#state'
34
import { createBatchRunner } from '#utils/batch'
45
import { getVersionsBatch } from 'fast-npm-meta'
5-
import { memoize } from '../memoize'
6+
import { defineCachedFunction } from 'ocache'
67

78
const BATCH_SIZE = 20
89

@@ -70,4 +71,8 @@ const getPackageInfoBatch = createBatchRunner<string, PackageInfo | null>({
7071
*
7172
* @see https://github.com/antfu/fast-npm-meta
7273
*/
73-
export const getPackageInfo = memoize<string, Promise<PackageInfo | null>>(async (name) => getPackageInfoBatch(name))
74+
export const getPackageInfo = defineCachedFunction<PackageInfo | null, [string]>(async (name) => getPackageInfoBatch(name), {
75+
name: 'package',
76+
getKey: (name) => name,
77+
maxAge: CACHE_MAX_AGE_ONE_DAY,
78+
})

src/api/replacement.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { ModuleReplacement } from 'module-replacements'
2+
import { CACHE_MAX_AGE_ONE_DAY, NPMX_DEV_API } from '#constants'
3+
import { logger } from '#state'
4+
import { encodePackageName } from '#utils/package'
5+
import { defineCachedFunction } from 'ocache'
6+
import { ofetch } from 'ofetch'
7+
8+
export const getReplacement = defineCachedFunction<ModuleReplacement | null, [string]>(async (name) => {
9+
logger.info(`[replacement] fetching for ${name}`)
10+
const encodedName = encodePackageName(name)
11+
12+
const result = await ofetch<ModuleReplacement | undefined>(`${NPMX_DEV_API}/replacements/${encodedName}`, {
13+
ignoreResponseError: true,
14+
}) ?? null
15+
logger.info(`[replacement] fetched for ${name}`)
16+
17+
return result
18+
}, {
19+
name: 'replacement',
20+
getKey: (name) => name,
21+
maxAge: CACHE_MAX_AGE_ONE_DAY,
22+
})
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { NPMX_DEV_API } from '#constants'
1+
import { CACHE_MAX_AGE_ONE_DAY, NPMX_DEV_API } from '#constants'
22
import { logger } from '#state'
3+
import { encodePackageName, formatPackageId } from '#utils/package'
4+
import { defineCachedFunction } from 'ocache'
35
import { ofetch } from 'ofetch'
4-
import { memoize } from '../memoize'
5-
import { encodePackageName, formatPackageId } from '../package'
66

77
/**
88
* Severity levels in priority order (highest first)
@@ -89,17 +89,18 @@ export interface VulnerabilityTreeResult {
8989
}
9090
}
9191

92-
export const getVulnerability = memoize<{
93-
name: string
94-
version: string
95-
}, Promise<VulnerabilityTreeResult>>(async ({ name, version }) => {
96-
logger.info(`Fetching vulnerabilities for ${formatPackageId(name, version)}`)
92+
export const getVulnerability = defineCachedFunction<VulnerabilityTreeResult | null, [name: string, version: string]>(async (name, version) => {
93+
logger.info(`[vulnerability] fetching for ${formatPackageId(name, version)}`)
9794
const encodedName = encodePackageName(name)
9895

99-
const result = await ofetch(`${NPMX_DEV_API}/registry/vulnerabilities/${encodedName}/v/${version}`)
100-
logger.info(`Fetched vulnerabilities for ${name}`)
96+
const result = await ofetch(`${NPMX_DEV_API}/registry/vulnerabilities/${encodedName}/v/${version}`, {
97+
ignoreResponseError: true,
98+
}) ?? null
99+
logger.info(`[vulnerability] fetched for ${name}`)
101100

102101
return result
103102
}, {
104-
getKey: ({ name, version }) => formatPackageId(name, version),
103+
name: 'vulnerability',
104+
getKey: (name, version) => formatPackageId(name, version),
105+
maxAge: CACHE_MAX_AGE_ONE_DAY,
105106
})

src/composables/workspace-context.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@ export function useWorkspaceContext() {
2222
if (!ctx)
2323
return
2424

25-
ctx.loadPackageManifestInfo.delete(uri)
26-
ctx.loadWorkspaceCatalogInfo.delete(uri)
27-
logger.info(`[workspace-context] delete dependencies cache: ${uri.path}`)
25+
ctx.invalidateDependencyInfo(uri)
26+
logger.info(`[workspace-context] invalidate dependencies cache: ${uri.path}`)
2827
if (reload && isWorkspaceLevelFile(uri)) {
2928
await ctx.loadWorkspace()
3029
}

src/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const SUPPORTED_DOCUMENT_PATTERN = `**/{${PACKAGE_JSON_BASENAME},${PNPM_W
66

77
export const PRERELEASE_PATTERN = /-.+/
88

9-
export const CACHE_TTL_ONE_DAY = 1000 * 60 * 60 * 24
9+
export const CACHE_MAX_AGE_ONE_DAY = 60 * 60 * 24
1010

1111
export const NPMX_DEV = 'https://npmx.dev'
1212
export const NPMX_DEV_API = `${NPMX_DEV}/api`

src/core/workspace.ts

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import type { CatalogsInfo, PackageManager, ResolvedDependencyInfo } from '#types/context'
22
import type { DependencyInfo, PackageManifestInfo, WorkspaceCatalogInfo } from '#types/extractor'
3-
import type { MemoizeOptions } from '#utils/memoize'
3+
import type { CacheOptions } from 'ocache'
44
import type { WorkspaceFolder } from 'vscode'
5+
import { getPackageInfo } from '#api/package'
56
import { logger } from '#state'
6-
import { getPackageInfo } from '#utils/api/package'
77
import { isOffsetInRange } from '#utils/ast'
88
import { resolveDependencySpec } from '#utils/dependency'
99
import { getDocumentText, isPackageManifestPath, isWorkspaceFilePath } from '#utils/file'
10-
import { memoize } from '#utils/memoize'
1110
import { resolveExactVersion } from '#utils/package'
1211
import { detectPackageManager, workspaceFileMapping } from '#utils/package-manager'
1312
import { lazyInit } from '#utils/shared'
13+
import { defineCachedFunction } from 'ocache'
1414
import { Uri, workspace } from 'vscode'
1515
import { accessOk } from 'vscode-find-up'
1616
import { getExtractor } from './extractors'
@@ -23,6 +23,7 @@ class WorkspaceContext {
2323
folder: WorkspaceFolder
2424
packageManager: PackageManager = 'npm'
2525
#catalogs?: PromiseWithResolvers<CatalogsInfo | undefined>
26+
#invalidatedPaths = new Set<string>()
2627

2728
private constructor(folder: WorkspaceFolder) {
2829
this.folder = folder
@@ -53,11 +54,17 @@ class WorkspaceContext {
5354
}
5455
}
5556

56-
#memoizeOptions: MemoizeOptions<Uri> = {
57+
#cacheOptions: CacheOptions<any, [Uri]> = {
5758
getKey: (uri) => uri.path,
58-
ttl: false,
59-
maxSize: Number.POSITIVE_INFINITY,
60-
fallbackToCachedOnError: false,
59+
maxAge: 0,
60+
swr: false,
61+
staleMaxAge: 0,
62+
shouldInvalidateCache: (uri) => this.#invalidatedPaths.delete(uri.path),
63+
}
64+
65+
invalidateDependencyInfo(uri: Uri) {
66+
const path = uri.path
67+
this.#invalidatedPaths.add(path)
6168
}
6269

6370
#createResolvedDependencyInfo(dependency: DependencyInfo, catalogs?: CatalogsInfo): ResolvedDependencyInfo {
@@ -87,9 +94,9 @@ class WorkspaceContext {
8794
}
8895
}
8996

90-
loadPackageManifestInfo = memoize<
91-
Uri,
92-
Promise<WithResolvedDependencyInfo<PackageManifestInfo> | undefined>
97+
loadPackageManifestInfo = defineCachedFunction<
98+
WithResolvedDependencyInfo<PackageManifestInfo> | undefined,
99+
[Uri]
93100
>(async (uri) => {
94101
const path = uri.path
95102
if (!isPackageManifestPath(path))
@@ -113,11 +120,11 @@ class WorkspaceContext {
113120
...info,
114121
dependencies: info.dependencies.map((dep) => this.#createResolvedDependencyInfo(dep, catalogs)),
115122
}
116-
}, this.#memoizeOptions)
123+
}, this.#cacheOptions)
117124

118-
loadWorkspaceCatalogInfo = memoize<
119-
Uri,
120-
Promise<WithResolvedDependencyInfo<WorkspaceCatalogInfo> | undefined>
125+
loadWorkspaceCatalogInfo = defineCachedFunction<
126+
WithResolvedDependencyInfo<WorkspaceCatalogInfo> | undefined,
127+
[Uri]
121128
>(async (uri) => {
122129
const path = uri.path
123130
if (!isWorkspaceFilePath(path))
@@ -138,20 +145,28 @@ class WorkspaceContext {
138145
...info,
139146
dependencies: info.dependencies.map((dep) => this.#createResolvedDependencyInfo(dep)),
140147
}
141-
}, this.#memoizeOptions)
148+
}, this.#cacheOptions)
142149
}
143150

144-
const getWorkspaceContextByFolder = memoize<WorkspaceFolder, Promise<WorkspaceContext | undefined>>(async (folder) => {
151+
const invalidatedFolderPaths = new Set<string>()
152+
153+
const getWorkspaceContextByFolder = defineCachedFunction<
154+
WorkspaceContext | undefined,
155+
[WorkspaceFolder]
156+
> (async (folder) => {
145157
logger.info(`[workspace-context] built ${folder.uri.path}`)
146158
return await WorkspaceContext.create(folder)
147159
}, {
160+
name: 'workspace-context',
148161
getKey: (folder) => folder.uri.path,
149-
ttl: false,
150-
fallbackToCachedOnError: false,
162+
swr: false,
163+
maxAge: 0,
164+
staleMaxAge: 0,
165+
shouldInvalidateCache: (folder) => invalidatedFolderPaths.delete(folder.uri.path),
151166
})
152167

153168
export function deleteWorkspaceContextCache(folder: WorkspaceFolder) {
154-
getWorkspaceContextByFolder.delete(folder)
169+
invalidatedFolderPaths.add(folder.uri.path)
155170
}
156171

157172
export async function getWorkspaceContext(uri: Uri) {

src/providers/diagnostics/rules/replacement.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ModuleReplacement } from 'module-replacements'
22
import type { DiagnosticRule } from '..'
3+
import { getReplacement } from '#api/replacement'
34
import { config } from '#state'
4-
import { getReplacement } from '#utils/api/replacement'
55
import { checkIgnored } from '#utils/ignore'
66
import { DiagnosticSeverity, Uri } from 'vscode'
77

0 commit comments

Comments
 (0)