1- import type { CatalogsInfo , ResolvedDependencyInfo , WorkspaceContext } from '#types/context'
1+ import type { CatalogsInfo , PackageManager , ResolvedDependencyInfo } from '#types/context'
22import type { DependencyInfo , PackageManifestInfo , WorkspaceCatalogInfo } from '#types/extractor'
3- import type { MemoizedFunction } from '#utils/memoize'
3+ import type { MemoizeOptions } from '#utils/memoize'
44import type { WorkspaceFolder } from 'vscode'
55import { packageManifestExtractorEntry , workspaceCatalogExtractorEntries } from '#extractors'
66import { logger } from '#state'
@@ -12,69 +12,99 @@ import { resolveExactVersion } from '#utils/package'
1212import { detectPackageManager } from '#utils/package-manager'
1313import { Uri , workspace } from 'vscode'
1414import { getDocumentText } from './document'
15+ import { lazyInit } from './shared'
1516
1617type WithResolvedDependencyInfo < T > = Omit < T , 'dependencies' > & {
1718 dependencies : ResolvedDependencyInfo [ ]
1819}
1920
20- interface WorkspaceContextState {
21- folder : WorkspaceFolder
22- workspaceContext : WorkspaceContext
23- loadPackageManifestInfo : MemoizedFunction < Uri , Promise < WithResolvedDependencyInfo < PackageManifestInfo > | undefined > >
24- loadWorkspaceCatalogInfo : MemoizedFunction < Uri , Promise < WithResolvedDependencyInfo < WorkspaceCatalogInfo > | undefined > >
25- }
26-
2721export function isPackageManifestPath ( path : string ) {
2822 return path . endsWith ( `/${ packageManifestExtractorEntry . basename } ` )
2923}
3024
31- function lazyInit < T > ( factory : ( ) => T ) : ( ) => T {
32- let cached : { value : T } | undefined
33- return ( ) => {
34- if ( ! cached )
35- cached = { value : factory ( ) }
36- return cached . value
25+ class WorkspaceContext {
26+ folder : WorkspaceFolder
27+ packageManager : PackageManager = 'npm'
28+ catalogs ?: CatalogsInfo
29+
30+ constructor ( folder : WorkspaceFolder ) {
31+ this . folder = folder
32+ this . #init( )
3733 }
38- }
3934
40- function createResolvedDependencyInfo (
41- dependency : DependencyInfo ,
42- catalogs ?: CatalogsInfo ,
43- ) : ResolvedDependencyInfo {
44- const resolution = resolveDependencySpec ( dependency . rawName , dependency . rawSpec , catalogs )
45-
46- const packageInfo = lazyInit (
47- async ( ) => resolution . resolvedProtocol === 'npm'
48- ? await getPackageInfo ( resolution . resolvedName ) ?? null
49- : null ,
50- )
51-
52- return {
53- ...dependency ,
54- ...resolution ,
55- categoryName : dependency . categoryName ?? resolution . categoryName ,
56- packageInfo,
57- resolvedVersion : lazyInit ( async ( ) => {
58- if ( resolution . resolvedProtocol !== 'npm' )
59- return null
60-
61- const pkg = await packageInfo ( )
62- if ( ! pkg )
63- return null
64-
65- return resolveExactVersion ( pkg , resolution . resolvedSpec )
66- } ) ,
35+ async #init( ) {
36+ this . packageManager = await detectPackageManager ( this . folder )
37+
38+ if ( this . packageManager !== 'npm' ) {
39+ const workspaceFilename = workspaceCatalogExtractorEntries . find (
40+ ( entry ) => this . packageManager === entry . packageManager ,
41+ ) ! . basename
42+ const workspaceFile = Uri . joinPath (
43+ this . folder . uri ,
44+ workspaceFilename ,
45+ )
46+ this . catalogs = ( await this . loadWorkspaceCatalogInfo ( workspaceFile ) ) ?. catalogs
47+ }
6748 }
68- }
6949
70- export const getWorkspaceContextState = memoize < Uri , Promise < WorkspaceContextState | undefined > > ( async ( uri ) => {
71- const folder = workspace . getWorkspaceFolder ( uri )
72- if ( ! folder )
73- return
50+ #memoizeOptions: MemoizeOptions < Uri > = {
51+ getKey : ( uri ) => uri . path ,
52+ ttl : false ,
53+ maxSize : Number . POSITIVE_INFINITY ,
54+ fallbackToCachedOnError : false ,
55+ }
56+
57+ #createResolvedDependencyInfo( dependency : DependencyInfo ) : ResolvedDependencyInfo {
58+ const resolution = resolveDependencySpec ( dependency . rawName , dependency . rawSpec , this . catalogs )
59+
60+ const packageInfo = lazyInit (
61+ async ( ) => resolution . resolvedProtocol === 'npm'
62+ ? await getPackageInfo ( resolution . resolvedName ) ?? null
63+ : null ,
64+ )
65+
66+ return {
67+ ...dependency ,
68+ ...resolution ,
69+ categoryName : dependency . categoryName ?? resolution . categoryName ,
70+ packageInfo,
71+ resolvedVersion : lazyInit ( async ( ) => {
72+ if ( resolution . resolvedProtocol !== 'npm' )
73+ return null
74+
75+ const pkg = await packageInfo ( )
76+ if ( ! pkg )
77+ return null
78+
79+ return resolveExactVersion ( pkg , resolution . resolvedSpec )
80+ } ) ,
81+ }
82+ }
83+
84+ loadPackageManifestInfo = memoize <
85+ Uri ,
86+ Promise < WithResolvedDependencyInfo < PackageManifestInfo > | undefined >
87+ > ( async ( uri ) => {
88+ if ( ! isPackageManifestPath ( uri . path ) )
89+ return
7490
75- const packageManager = await detectPackageManager ( folder )
91+ logger . info ( `[workspace-context] load package manifest info: ${ uri . path } ` )
92+ const text = await getDocumentText ( uri )
7693
77- const loadWorkspaceCatalogInfo = memoize ( async ( uri : Uri ) : Promise < WithResolvedDependencyInfo < WorkspaceCatalogInfo > | undefined > => {
94+ const info = packageManifestExtractorEntry . extractor . getPackageManifestInfo ( text )
95+ if ( ! info )
96+ return
97+
98+ return {
99+ ...info ,
100+ dependencies : info . dependencies . map ( this . #createResolvedDependencyInfo) ,
101+ }
102+ } , this . #memoizeOptions)
103+
104+ loadWorkspaceCatalogInfo = memoize <
105+ Uri ,
106+ Promise < WithResolvedDependencyInfo < WorkspaceCatalogInfo > | undefined >
107+ > ( async ( uri ) => {
78108 const path = uri . path
79109 logger . info ( `[workspace-context] load workspace catalog info: ${ path } ` )
80110
@@ -90,62 +120,34 @@ export const getWorkspaceContextState = memoize<Uri, Promise<WorkspaceContextSta
90120
91121 return {
92122 ...info ,
93- dependencies : info . dependencies . map ( ( dependency ) => createResolvedDependencyInfo ( dependency ) ) ,
123+ dependencies : info . dependencies . map ( this . # createResolvedDependencyInfo) ,
94124 }
95125 }
96- } , { getKey : ( uri ) => uri . path , ttl : false , maxSize : Number . POSITIVE_INFINITY , fallbackToCachedOnError : false } )
97-
98- let catalogs : CatalogsInfo | undefined
126+ } , this . #memoizeOptions)
127+ }
99128
100- if ( packageManager !== 'npm' ) {
101- const workspaceFile = Uri . joinPath (
102- folder . uri ,
103- workspaceCatalogExtractorEntries . find ( ( entry ) => packageManager === entry . packageManager ) ! . basename ,
104- )
105- catalogs = ( await loadWorkspaceCatalogInfo ( workspaceFile ) ) ?. catalogs
106- }
129+ export const getWorkspaceContext = memoize < Uri , Promise < WorkspaceContext | undefined > > ( async ( uri ) => {
130+ const folder = workspace . getWorkspaceFolder ( uri )
131+ if ( ! folder )
132+ return
107133
108134 logger . info ( `[workspace-context] built ${ folder . uri . path } ` )
109-
110- return {
111- folder,
112- workspaceContext : {
113- packageManager,
114- catalogs,
115- } ,
116- loadPackageManifestInfo : memoize ( async ( uri : Uri ) => {
117- if ( ! isPackageManifestPath ( uri . path ) )
118- return
119-
120- logger . info ( `[workspace-context] load package manifest info: ${ uri . path } ` )
121- const text = await getDocumentText ( uri )
122-
123- const info = packageManifestExtractorEntry . extractor . getPackageManifestInfo ( text )
124- if ( ! info )
125- return
126-
127- return {
128- ...info ,
129- dependencies : info . dependencies . map ( ( dependency ) => createResolvedDependencyInfo ( dependency , catalogs ) ) ,
130- }
131- } , { getKey : ( uri ) => uri . path , ttl : false , maxSize : Number . POSITIVE_INFINITY , fallbackToCachedOnError : false } ) ,
132- loadWorkspaceCatalogInfo,
133- }
135+ return new WorkspaceContext ( folder )
134136} , {
135137 getKey : ( uri : Uri ) => workspace . getWorkspaceFolder ( uri ) ! . uri . path ,
136138 ttl : false ,
137139 fallbackToCachedOnError : false ,
138140} )
139141
140142export async function getResolvedDependencies ( uri : Uri ) : Promise < ResolvedDependencyInfo [ ] | undefined > {
141- const state = await getWorkspaceContextState ( uri )
142- if ( ! state )
143+ const ctx = await getWorkspaceContext ( uri )
144+ if ( ! ctx )
143145 return [ ]
144146
145147 return (
146148 isPackageManifestPath ( uri . path )
147- ? await state . loadPackageManifestInfo ( uri )
148- : await state . loadWorkspaceCatalogInfo ( uri )
149+ ? await ctx . loadPackageManifestInfo ( uri )
150+ : await ctx . loadWorkspaceCatalogInfo ( uri )
149151 ) ?. dependencies
150152}
151153
0 commit comments