@@ -33,6 +33,7 @@ import { OVSXClientProvider } from '../common/ovsx-client-provider';
33
33
import { RequestContext , RequestService } from '@theia/core/shared/@theia/request' ;
34
34
import { OVSXApiFilterProvider } from '@theia/ovsx-client' ;
35
35
import { ApplicationServer } from '@theia/core/lib/common/application-protocol' ;
36
+ import { FileService } from '@theia/filesystem/lib/browser/file-service' ;
36
37
37
38
@injectable ( )
38
39
export class VSXExtensionsModel {
@@ -81,6 +82,9 @@ export class VSXExtensionsModel {
81
82
@inject ( OVSXApiFilterProvider )
82
83
protected vsxApiFilter : OVSXApiFilterProvider ;
83
84
85
+ @inject ( FileService )
86
+ protected readonly fileService : FileService ;
87
+
84
88
@inject ( ApplicationServer )
85
89
protected readonly applicationServer : ApplicationServer ;
86
90
@@ -135,13 +139,24 @@ export class VSXExtensionsModel {
135
139
resolve ( id : string ) : Promise < VSXExtension > {
136
140
return this . doChange ( async ( ) => {
137
141
await this . initialized ;
138
- const extension = await this . refresh ( id ) ;
142
+ const extension = await this . refresh ( id ) ?? this . getExtension ( id ) ;
139
143
if ( ! extension ) {
140
144
throw new Error ( `Failed to resolve ${ id } extension.` ) ;
141
145
}
142
- if ( extension . readmeUrl ) {
146
+ if ( extension . readme === undefined ) {
143
147
try {
144
- const rawReadme = RequestContext . asText ( await this . request . request ( { url : extension . readmeUrl } ) ) ;
148
+ let rawReadme : string = '' ;
149
+ const installedReadme = await this . findReadmeFile ( extension ) ;
150
+ // Attempt to read the local readme first
151
+ // It saves network resources and is faster
152
+ if ( installedReadme ) {
153
+ const readmeContent = await this . fileService . readFile ( installedReadme ) ;
154
+ rawReadme = readmeContent . value . toString ( ) ;
155
+ } else if ( extension . readmeUrl ) {
156
+ rawReadme = RequestContext . asText (
157
+ await this . request . request ( { url : extension . readmeUrl } )
158
+ ) ;
159
+ }
145
160
const readme = this . compileReadme ( rawReadme ) ;
146
161
extension . update ( { readme } ) ;
147
162
} catch ( e ) {
@@ -154,6 +169,22 @@ export class VSXExtensionsModel {
154
169
} ) ;
155
170
}
156
171
172
+ protected async findReadmeFile ( extension : VSXExtension ) : Promise < URI | undefined > {
173
+ if ( ! extension . plugin ) {
174
+ return undefined ;
175
+ }
176
+ // Since we don't know the exact capitalization of the readme file (might be README.md, readme.md, etc.)
177
+ // We attempt to find the readme file by searching through the plugin's directories
178
+ const packageUri = new URI ( extension . plugin . metadata . model . packageUri ) ;
179
+ const pluginUri = packageUri . withPath ( packageUri . path . join ( '..' ) ) ;
180
+ const pluginDirStat = await this . fileService . resolve ( pluginUri ) ;
181
+ const possibleNames = [ 'readme.md' , 'readme.txt' , 'readme' ] ;
182
+ const readmeFileUri = pluginDirStat . children
183
+ ?. find ( child => possibleNames . includes ( child . name . toLowerCase ( ) ) )
184
+ ?. resource ;
185
+ return readmeFileUri ;
186
+ }
187
+
157
188
protected async initInstalled ( ) : Promise < void > {
158
189
await this . pluginSupport . willStart ;
159
190
this . pluginSupport . onDidChangePlugins ( ( ) => this . updateInstalled ( ) ) ;
0 commit comments