1
1
import assert from 'node:assert' ;
2
- import * as crypto from 'node:crypto' ;
3
- import * as fs from 'node:fs' ;
2
+ import crypto from 'node:crypto' ;
3
+ import fs from 'node:fs/promises ' ;
4
4
import {
5
5
isAbsolute as isAbsolutePath ,
6
6
relative as relativePath ,
9
9
} from 'node:path' ;
10
10
import { fileURLToPath } from 'node:url' ;
11
11
12
+ import { isUrlLike , toFilePathOrHref } from '@cspell/url' ;
13
+
12
14
import { readFileInfo } from '../../util/fileHelper.js' ;
13
15
import type { LintFileResult } from '../../util/LintFileResult.js' ;
14
16
import type { CSpellLintResultCache } from './CSpellLintResultCache.js' ;
@@ -109,7 +111,7 @@ export class DiskCache implements CSpellLintResultCache {
109
111
! meta ||
110
112
! result ||
111
113
! versionMatches ||
112
- ! this . checkDependencies ( data . d )
114
+ ! ( await this . checkDependencies ( data . d ) )
113
115
) {
114
116
return undefined ;
115
117
}
@@ -148,7 +150,7 @@ export class DiskCache implements CSpellLintResultCache {
148
150
const data : CachedData = this . objectCollection . get ( {
149
151
v : this . version ,
150
152
r : this . normalizeResult ( result ) ,
151
- d : this . calcDependencyHashes ( dependsUponFiles ) ,
153
+ d : await this . calcDependencyHashes ( dependsUponFiles ) ,
152
154
} ) ;
153
155
154
156
meta . data = data ;
@@ -174,27 +176,27 @@ export class DiskCache implements CSpellLintResultCache {
174
176
return this . ocCacheFileResult . get ( { issues, processed, errors, configErrors, reportIssueOptions } ) ;
175
177
}
176
178
177
- private calcDependencyHashes ( dependsUponFiles : string [ ] ) : Dependency [ ] {
179
+ private async calcDependencyHashes ( dependsUponFiles : string [ ] ) : Promise < Dependency [ ] > {
178
180
dependsUponFiles . sort ( ) ;
179
181
180
182
const c = getTreeEntry ( this . dependencyCacheTree , dependsUponFiles ) ;
181
183
if ( c ?. d ) {
182
184
return c . d ;
183
185
}
184
186
185
- const dependencies : Dependency [ ] = dependsUponFiles . map ( ( f ) => this . getDependency ( f ) ) ;
187
+ const dependencies : Dependency [ ] = await Promise . all ( dependsUponFiles . map ( ( f ) => this . getDependency ( f ) ) ) ;
186
188
187
189
return setTreeEntry ( this . dependencyCacheTree , dependencies ) ;
188
190
}
189
191
190
- private checkDependency ( dep : Dependency ) : boolean {
192
+ private async checkDependency ( dep : Dependency ) : Promise < boolean > {
191
193
const depFile = this . resolveFile ( dep . f ) ;
192
194
const cDep = this . dependencyCache . get ( depFile ) ;
193
195
194
196
if ( cDep && compDep ( dep , cDep ) ) return true ;
195
197
if ( cDep ) return false ;
196
198
197
- const d = this . getFileDep ( depFile ) ;
199
+ const d = await this . getFileDep ( depFile ) ;
198
200
if ( compDep ( dep , d ) ) {
199
201
this . dependencyCache . set ( depFile , dep ) ;
200
202
return true ;
@@ -203,31 +205,37 @@ export class DiskCache implements CSpellLintResultCache {
203
205
return false ;
204
206
}
205
207
206
- private getDependency ( file : string ) : Dependency {
208
+ private async getDependency ( file : string ) : Promise < Dependency > {
207
209
const dep = this . dependencyCache . get ( file ) ;
208
210
if ( dep ) return dep ;
209
- const d = this . getFileDep ( file ) ;
211
+ const d = await this . getFileDep ( file ) ;
210
212
this . dependencyCache . set ( file , d ) ;
211
213
return d ;
212
214
}
213
215
214
- private getFileDep ( file : string ) : Dependency {
216
+ private async getFileDep ( file : string ) : Promise < Dependency > {
217
+ if ( isUrlLike ( file ) ) {
218
+ if ( ! file . startsWith ( 'file://' ) ) {
219
+ return getDependencyForUrl ( file ) ;
220
+ }
221
+ file = toFilePathOrHref ( file ) ;
222
+ }
215
223
assert ( isAbsolutePath ( file ) , `Dependency must be absolute "${ file } "` ) ;
216
224
const f = this . toRelFile ( file ) ;
217
225
let h : string ;
218
226
try {
219
- const buffer = fs . readFileSync ( file ) ;
227
+ const buffer = await fs . readFile ( file ) ;
220
228
h = this . getHash ( buffer ) ;
221
229
} catch {
222
230
return { f } ;
223
231
}
224
232
return { f, h } ;
225
233
}
226
234
227
- private checkDependencies ( dependencies : Dependency [ ] | undefined ) : boolean {
235
+ private async checkDependencies ( dependencies : Dependency [ ] | undefined ) : Promise < boolean > {
228
236
if ( ! dependencies ) return false ;
229
237
for ( const dep of dependencies ) {
230
- if ( ! this . checkDependency ( dep ) ) {
238
+ if ( ! ( await this . checkDependency ( dep ) ) ) {
231
239
return false ;
232
240
}
233
241
}
@@ -239,6 +247,9 @@ export class DiskCache implements CSpellLintResultCache {
239
247
}
240
248
241
249
private resolveFile ( file : string ) : string {
250
+ if ( isUrlLike ( file ) ) {
251
+ return file ;
252
+ }
242
253
return normalizePath ( resolvePath ( this . cacheDir , file ) ) ;
243
254
}
244
255
@@ -247,6 +258,24 @@ export class DiskCache implements CSpellLintResultCache {
247
258
}
248
259
}
249
260
261
+ async function getDependencyForUrl ( remoteUrl : string | URL ) : Promise < Dependency > {
262
+ const url = new URL ( remoteUrl ) ;
263
+
264
+ try {
265
+ // eslint-disable-next-line n/no-unsupported-features/node-builtins
266
+ const response = await fetch ( url , { method : 'HEAD' } ) ;
267
+ const h =
268
+ response . headers . get ( 'etag' ) ||
269
+ response . headers . get ( 'last-modified' ) ||
270
+ response . headers . get ( 'content-length' ) ||
271
+ '' ;
272
+ return { f : url . href , h : h ? h . trim ( ) : '' } ;
273
+ } catch {
274
+ // If the fetch fails, we cannot compute a hash, so we return an empty hash.
275
+ return { f : url . href , h : '' } ;
276
+ }
277
+ }
278
+
250
279
export async function createDiskCache (
251
280
cacheFileLocation : URL ,
252
281
useCheckSum : boolean ,
@@ -304,6 +333,8 @@ export function normalizePath(filePath: string): string {
304
333
305
334
export const __testing__ : {
306
335
calcVersion : typeof calcVersion ;
336
+ getDependencyForUrl : typeof getDependencyForUrl ;
307
337
} = {
308
338
calcVersion,
339
+ getDependencyForUrl,
309
340
} ;
0 commit comments