cspell-io 8.2.1 → 8.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import type { CSpellIO } from './CSpellIO.js';
2
- import { type DirEntry, type Disposable, type FileReference, type FileResource, type Stats } from './models/index.js';
2
+ import type { BufferEncoding, DirEntry, Disposable, FileReference, FileResource, Stats, TextFileResource } from './models/index.js';
3
3
  type UrlOrReference = URL | FileReference;
4
4
  type NextProvider = (url: URL) => VProviderFileSystem | undefined;
5
5
  export interface VirtualFS extends Disposable {
@@ -16,6 +16,11 @@ export interface VirtualFS extends Disposable {
16
16
  * Clear the cache of file systems.
17
17
  */
18
18
  reset(): void;
19
+ /**
20
+ * Indicates that logging has been enabled.
21
+ */
22
+ loggingEnabled: boolean;
23
+ enableLogging(value?: boolean): void;
19
24
  }
20
25
  export declare enum FSCapabilityFlags {
21
26
  None = 0,
@@ -30,22 +35,54 @@ export declare enum FSCapabilityFlags {
30
35
  interface FileSystemProviderInfo {
31
36
  name: string;
32
37
  }
33
- interface FileSystemBase {
34
- readFile(url: UrlOrReference): Promise<FileResource>;
38
+ export interface VFileSystem {
39
+ /**
40
+ * Read a file.
41
+ * @param url - URL to read
42
+ * @param encoding - optional encoding
43
+ * @returns A FileResource, the content will not be decoded. Use `.getText()` to get the decoded text.
44
+ */
45
+ readFile(url: UrlOrReference, encoding?: BufferEncoding): Promise<TextFileResource>;
46
+ /**
47
+ * Write a file
48
+ * @param file - the file to write
49
+ */
35
50
  writeFile(file: FileResource): Promise<FileReference>;
36
51
  /**
37
- * Information about the provider.
38
- * It is up to the provider to define what information is available.
52
+ * Get the stats for a url.
53
+ * @param url - Url to fetch stats for.
39
54
  */
40
- providerInfo: FileSystemProviderInfo;
41
- }
42
- export interface VFileSystem extends FileSystemBase {
43
55
  stat(url: UrlOrReference): Promise<VfsStat>;
56
+ /**
57
+ * Read the directory entries for a url.
58
+ * The url should end with `/` to indicate a directory.
59
+ * @param url - the url to read the directory entries for.
60
+ */
44
61
  readDirectory(url: URL): Promise<VfsDirEntry[]>;
62
+ /**
63
+ * Get the capabilities for a URL.
64
+ * The capabilities can be more restrictive than the general capabilities of the provider.
65
+ * @param url - the url to try
66
+ */
45
67
  getCapabilities(url: URL): FSCapabilities;
68
+ /**
69
+ * Information about the provider.
70
+ * It is up to the provider to define what information is available.
71
+ */
72
+ providerInfo: FileSystemProviderInfo;
73
+ /**
74
+ * Indicates that a provider was found for the url.
75
+ */
46
76
  hasProvider: boolean;
47
77
  }
48
- export interface VProviderFileSystem extends FileSystemBase, Disposable {
78
+ export interface VProviderFileSystem extends Disposable {
79
+ readFile(url: UrlOrReference): Promise<FileResource>;
80
+ writeFile(file: FileResource): Promise<FileReference>;
81
+ /**
82
+ * Information about the provider.
83
+ * It is up to the provider to define what information is available.
84
+ */
85
+ providerInfo: FileSystemProviderInfo;
49
86
  stat(url: UrlOrReference): Stats | Promise<Stats>;
50
87
  readDirectory(url: URL): Promise<DirEntry[]>;
51
88
  /**
@@ -1,6 +1,7 @@
1
- import { urlOrReferenceToUrl } from './common/index.js';
1
+ import { createTextFileResource, urlOrReferenceToUrl } from './common/index.js';
2
2
  import { getDefaultCSpellIO } from './CSpellIONode.js';
3
- import { FileType, } from './models/index.js';
3
+ import { FileType } from './models/index.js';
4
+ const debug = false;
4
5
  export var FSCapabilityFlags;
5
6
  (function (FSCapabilityFlags) {
6
7
  FSCapabilityFlags[FSCapabilityFlags["None"] = 0] = "None";
@@ -17,9 +18,21 @@ class CVirtualFS {
17
18
  cachedFs = new Map();
18
19
  revCacheFs = new Map();
19
20
  fs;
21
+ loggingEnabled = debug;
20
22
  constructor() {
21
23
  this.fs = fsPassThrough((url) => this._getFS(url));
22
24
  }
25
+ enableLogging(value) {
26
+ this.loggingEnabled = value ?? true;
27
+ }
28
+ log = console.log;
29
+ logEvent = (event) => {
30
+ if (this.loggingEnabled) {
31
+ const id = event.traceID.toFixed(13).replace(/\d{4}(?=\d)/g, '$&.');
32
+ const msg = event.message ? `\n\t\t${event.message}` : '';
33
+ this.log(`${event.method}-${event.event}\t ID:${id} ts:${event.ts.toFixed(13)} ${chopUrl(event.url)}${msg}`);
34
+ }
35
+ };
23
36
  registerFileSystemProvider(...providers) {
24
37
  providers.forEach((provider) => this.providers.add(provider));
25
38
  this.reset();
@@ -67,7 +80,7 @@ class CVirtualFS {
67
80
  for (const provider of this.providers) {
68
81
  next = fnNext(provider, next);
69
82
  }
70
- const fs = new WrappedProviderFs(next(url));
83
+ const fs = new WrappedProviderFs(next(url), this.logEvent);
71
84
  this.cachedFs.set(key, fs);
72
85
  return fs;
73
86
  }
@@ -211,57 +224,89 @@ export function fsCapabilities(flags) {
211
224
  }
212
225
  class WrappedProviderFs {
213
226
  fs;
227
+ eventLogger;
214
228
  hasProvider;
215
229
  capabilities;
216
230
  providerInfo;
217
231
  _capabilities;
218
- constructor(fs) {
232
+ constructor(fs, eventLogger) {
219
233
  this.fs = fs;
234
+ this.eventLogger = eventLogger;
220
235
  this.hasProvider = !!fs;
221
236
  this.capabilities = fs?.capabilities || FSCapabilityFlags.None;
222
237
  this._capabilities = fsCapabilities(this.capabilities);
223
238
  this.providerInfo = fs?.providerInfo || { name: 'unknown' };
224
239
  }
240
+ logEvent(method, event, traceID, url, message) {
241
+ this.eventLogger({ method, event, url, traceID, ts: performance.now(), message });
242
+ }
225
243
  getCapabilities(url) {
226
244
  if (this.fs?.getCapabilities)
227
245
  return this.fs.getCapabilities(url);
228
246
  return this._capabilities;
229
247
  }
230
- async stat(url) {
248
+ async stat(urlRef) {
249
+ const traceID = performance.now();
250
+ const url = urlOrReferenceToUrl(urlRef);
251
+ this.logEvent('stat', 'start', traceID, url);
231
252
  try {
232
- checkCapabilityOrThrow(this.fs, this.capabilities, FSCapabilityFlags.Stat, 'stat', urlOrReferenceToUrl(url));
233
- return new CVfsStat(await this.fs.stat(url));
253
+ checkCapabilityOrThrow(this.fs, this.capabilities, FSCapabilityFlags.Stat, 'stat', url);
254
+ return new CVfsStat(await this.fs.stat(urlRef));
234
255
  }
235
256
  catch (e) {
257
+ this.logEvent('stat', 'error', traceID, url, e instanceof Error ? e.message : '');
236
258
  throw wrapError(e);
237
259
  }
260
+ finally {
261
+ this.logEvent('stat', 'end', traceID, url);
262
+ }
238
263
  }
239
- async readFile(url) {
264
+ async readFile(urlRef, encoding) {
265
+ const traceID = performance.now();
266
+ const url = urlOrReferenceToUrl(urlRef);
267
+ this.logEvent('readFile', 'start', traceID, url);
240
268
  try {
241
- checkCapabilityOrThrow(this.fs, this.capabilities, FSCapabilityFlags.Read, 'readFile', urlOrReferenceToUrl(url));
242
- return await this.fs.readFile(url);
269
+ checkCapabilityOrThrow(this.fs, this.capabilities, FSCapabilityFlags.Read, 'readFile', url);
270
+ return createTextFileResource(await this.fs.readFile(urlRef), encoding);
243
271
  }
244
272
  catch (e) {
273
+ this.logEvent('readFile', 'error', traceID, url, e instanceof Error ? e.message : '');
245
274
  throw wrapError(e);
246
275
  }
276
+ finally {
277
+ this.logEvent('readFile', 'end', traceID, url);
278
+ }
247
279
  }
248
280
  async readDirectory(url) {
281
+ const traceID = performance.now();
282
+ this.logEvent('readDir', 'start', traceID, url);
249
283
  try {
250
284
  checkCapabilityOrThrow(this.fs, this.capabilities, FSCapabilityFlags.ReadDir, 'readDirectory', url);
251
285
  return (await this.fs.readDirectory(url)).map((e) => new CVfsDirEntry(e));
252
286
  }
253
287
  catch (e) {
288
+ this.logEvent('readDir', 'error', traceID, url, e instanceof Error ? e.message : '');
254
289
  throw wrapError(e);
255
290
  }
291
+ finally {
292
+ this.logEvent('readDir', 'end', traceID, url);
293
+ }
256
294
  }
257
295
  async writeFile(file) {
296
+ const traceID = performance.now();
297
+ const url = file.url;
298
+ this.logEvent('writeFile', 'start', traceID, url);
258
299
  try {
259
300
  checkCapabilityOrThrow(this.fs, this.capabilities, FSCapabilityFlags.Write, 'writeFile', file.url);
260
301
  return await this.fs.writeFile(file);
261
302
  }
262
303
  catch (e) {
304
+ this.logEvent('writeFile', 'error', traceID, url, e instanceof Error ? e.message : '');
263
305
  throw wrapError(e);
264
306
  }
307
+ finally {
308
+ this.logEvent('writeFile', 'end', traceID, url);
309
+ }
265
310
  }
266
311
  static disposeOf(fs) {
267
312
  fs instanceof WrappedProviderFs && fs.fs?.dispose();
@@ -323,4 +368,16 @@ class CVfsDirEntry extends CFileType {
323
368
  return this._url;
324
369
  }
325
370
  }
371
+ function chopUrl(url) {
372
+ if (!url)
373
+ return '';
374
+ const href = url.href;
375
+ const parts = href.split('/');
376
+ const n = parts.indexOf('node_modules');
377
+ if (n > 0) {
378
+ const tail = parts.slice(Math.max(parts.length - 3, n + 1));
379
+ return parts.slice(0, n + 1).join('/') + '/.../' + tail.join('/');
380
+ }
381
+ return href;
382
+ }
326
383
  //# sourceMappingURL=VirtualFS.js.map
@@ -9,7 +9,7 @@ export declare class CFileResource implements TextFileResource {
9
9
  private _gz?;
10
10
  constructor(url: URL, content: string | ArrayBufferView, encoding: BufferEncoding | undefined, baseFilename: string | undefined, gz: boolean | undefined);
11
11
  get gz(): boolean;
12
- getText(): string;
12
+ getText(encoding?: BufferEncoding): string;
13
13
  toJson(): {
14
14
  url: string;
15
15
  content: string;
@@ -23,6 +23,6 @@ export declare class CFileResource implements TextFileResource {
23
23
  static from(fileReference: FileReference | URL, content: string | ArrayBufferView): CFileResource;
24
24
  static from(url: URL, content: string | ArrayBufferView, encoding?: BufferEncoding | undefined, baseFilename?: string | undefined, gz?: boolean): CFileResource;
25
25
  }
26
- export declare function fromFileResource(fileResource: FileResource): TextFileResource;
26
+ export declare function fromFileResource(fileResource: FileResource, encoding?: BufferEncoding): TextFileResource;
27
27
  export declare function renameFileResource(fileResource: FileResource, url: URL): FileResource;
28
28
  //# sourceMappingURL=CFileResource.d.ts.map
@@ -23,10 +23,10 @@ export class CFileResource {
23
23
  return false;
24
24
  return isGZipped(this.content);
25
25
  }
26
- getText() {
26
+ getText(encoding) {
27
27
  if (this._text !== undefined)
28
28
  return this._text;
29
- const text = typeof this.content === 'string' ? this.content : decode(this.content, this.encoding);
29
+ const text = typeof this.content === 'string' ? this.content : decode(this.content, encoding ?? this.encoding);
30
30
  this._text = text;
31
31
  return text;
32
32
  }
@@ -63,10 +63,10 @@ export class CFileResource {
63
63
  return new CFileResource(fileResource.url, fileResource.content, fileResource.encoding, fileResource.baseFilename, fileResource.gz);
64
64
  }
65
65
  }
66
- export function fromFileResource(fileResource) {
67
- return CFileResource.from(fileResource);
66
+ export function fromFileResource(fileResource, encoding) {
67
+ return CFileResource.from(encoding ? { ...fileResource, encoding } : fileResource);
68
68
  }
69
69
  export function renameFileResource(fileResource, url) {
70
- return CFileResource.from(url, fileResource.content, fileResource.encoding, fileResource.baseFilename, fileResource.gz);
70
+ return CFileResource.from({ ...fileResource, url });
71
71
  }
72
72
  //# sourceMappingURL=CFileResource.js.map
@@ -1,4 +1,5 @@
1
1
  export { CFileReference, renameFileReference } from './CFileReference.js';
2
2
  export { CFileResource, fromFileResource as createTextFileResource, renameFileResource } from './CFileResource.js';
3
+ export { compareStats } from './stat.js';
3
4
  export { urlOrReferenceToUrl } from './urlOrReferenceToUrl.js';
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,4 +1,5 @@
1
1
  export { CFileReference, renameFileReference } from './CFileReference.js';
2
2
  export { CFileResource, fromFileResource as createTextFileResource, renameFileResource } from './CFileResource.js';
3
+ export { compareStats } from './stat.js';
3
4
  export { urlOrReferenceToUrl } from './urlOrReferenceToUrl.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  export { toArray as asyncIterableToArray } from './async/asyncIterable.js';
2
2
  export * from './common/index.js';
3
- export { createTextFileResource } from './common/index.js';
3
+ export { compareStats, createTextFileResource } from './common/index.js';
4
4
  export type { CSpellIO } from './CSpellIO.js';
5
5
  export { CSpellIONode, getDefaultCSpellIO } from './CSpellIONode.js';
6
6
  export { getStat, getStatSync, readFileText, readFileTextSync, writeToFile, writeToFileIterable, writeToFileIterableP, } from './file/index.js';
package/dist/esm/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export { toArray as asyncIterableToArray } from './async/asyncIterable.js';
2
2
  export * from './common/index.js';
3
- export { createTextFileResource } from './common/index.js';
3
+ export { compareStats, createTextFileResource } from './common/index.js';
4
4
  export { CSpellIONode, getDefaultCSpellIO } from './CSpellIONode.js';
5
5
  export { getStat, getStatSync, readFileText, readFileTextSync, writeToFile, writeToFileIterable, writeToFileIterableP, } from './file/index.js';
6
6
  export { FileType as VFileType } from './models/Stats.js';
@@ -27,7 +27,13 @@ export interface FileResource extends FileReference {
27
27
  readonly content: string | ArrayBufferView;
28
28
  }
29
29
  export interface TextFileResource extends FileResource {
30
- getText(): string;
30
+ /**
31
+ * Extract the text of the file.
32
+ * @param encoding - optional encoding to use when decoding the content.
33
+ * by default it uses the encoding of the file if one was specified, otherwise it uses `utf8`.
34
+ * If the content is a string, then the encoding is ignored.
35
+ */
36
+ getText(encoding?: BufferEncoding): string;
31
37
  }
32
38
  export type UrlOrFilename = string | URL;
33
39
  export type UrlOrReference = UrlOrFilename | FileReference;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cspell-io",
3
- "version": "8.2.1",
3
+ "version": "8.2.3",
4
4
  "description": "A library of useful I/O functions used across various cspell tools.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -51,7 +51,7 @@
51
51
  "typescript": "^5.3.3"
52
52
  },
53
53
  "dependencies": {
54
- "@cspell/cspell-service-bus": "8.2.1"
54
+ "@cspell/cspell-service-bus": "8.2.3"
55
55
  },
56
- "gitHead": "b0c889ee4068aa8a2447106c5c7f449debc85bdd"
56
+ "gitHead": "e3098b21e0a199d61226f8ff4989d48b385eddfa"
57
57
  }