@davidsouther/jiffies 2.0.6 → 2.1.0

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.
package/src/fs.test.ts CHANGED
@@ -1,5 +1,10 @@
1
- import { FileSystem, RecordFileSystemAdapter } from "./fs.js";
2
- import { describe, it, expect } from "./scope/index.js";
1
+ import {
2
+ FileSystem,
3
+ ObjectFileSystemAdapter,
4
+ RecordFileSystemAdapter,
5
+ } from "./fs.js";
6
+ import { describe, it, expect, beforeEach } from "./scope/index.js";
7
+ import { cleanState } from "./scope/state.js";
3
8
 
4
9
  describe("FileSystem", () => {
5
10
  describe("Writing", () => {
@@ -50,4 +55,48 @@ describe("FileSystem", () => {
50
55
  expect(dir.sort()).toEqual(["bonjour", "hello"]);
51
56
  });
52
57
  });
58
+
59
+ describe("stat", () => {
60
+ const state = cleanState(() => {
61
+ const fsObj = {
62
+ "/deep/hello": "world",
63
+ "/deep/bonjour": "monde",
64
+ "/other/file": "text",
65
+ };
66
+ return { fs: new FileSystem(new RecordFileSystemAdapter(fsObj)) };
67
+ }, beforeEach);
68
+
69
+ it("stats a directory", async () => {
70
+ const deep = await state.fs.stat("/deep");
71
+ expect(deep.isDirectory()).toBe(true);
72
+ expect(deep.isFile()).toBe(false);
73
+ });
74
+
75
+ it("stats a file", async () => {
76
+ const deep = await state.fs.stat("/deep/hello");
77
+ expect(deep.isDirectory()).toBe(false);
78
+ expect(deep.isFile()).toBe(true);
79
+ });
80
+ });
81
+
82
+ describe("ObjectFileSystem", () => {
83
+ it("treats object keys as directories and final values as strings", async () => {
84
+ let fs = new FileSystem(
85
+ new ObjectFileSystemAdapter({
86
+ deep: {
87
+ hello: "world",
88
+ bonjour: "monde",
89
+ },
90
+ other_file: "text",
91
+ })
92
+ );
93
+
94
+ const deep = await fs.stat("/deep");
95
+ expect(deep.isDirectory()).toBe(true);
96
+ expect(deep.isFile()).toBe(false);
97
+
98
+ const deep_bonjour = await fs.readFile("/deep/bonjour");
99
+ expect(deep_bonjour).toBe("monde");
100
+ });
101
+ });
53
102
  });
package/src/fs.ts CHANGED
@@ -8,9 +8,11 @@ export interface Stats {
8
8
  name: string;
9
9
  }
10
10
 
11
- export function basename(filename: PathLike) {
12
- const end = filename.lastIndexOf("/");
13
- const basename = filename.substring(end === -1 ? 0 : end);
11
+ export function basename(filename: PathLike): string {
12
+ if (filename.endsWith("/")) {
13
+ filename = filename.substring(0, filename.length - 1);
14
+ }
15
+ const basename = filename.split("/").at(-1) ?? "";
14
16
  return basename;
15
17
  }
16
18
 
@@ -108,8 +110,8 @@ export class RecordFileSystemAdapter implements FileSystemAdapter {
108
110
 
109
111
  stat(path: PathLike): Promise<Stats> {
110
112
  return new Promise((resolve, reject) => {
111
- if (this.fs[path] != null) {
112
- resolve({
113
+ if (this.fs[path]) {
114
+ return resolve({
113
115
  name: basename(path),
114
116
  isDirectory() {
115
117
  return false;
@@ -118,9 +120,24 @@ export class RecordFileSystemAdapter implements FileSystemAdapter {
118
120
  return true;
119
121
  },
120
122
  });
121
- } else {
122
- reject();
123
123
  }
124
+
125
+ if (!path.endsWith("/")) path += "/";
126
+ for (let filename of Object.keys(this.fs)) {
127
+ if (filename.startsWith(path)) {
128
+ return resolve({
129
+ name: basename(path),
130
+ isDirectory() {
131
+ return true;
132
+ },
133
+ isFile() {
134
+ return false;
135
+ },
136
+ });
137
+ }
138
+ }
139
+
140
+ reject();
124
141
  });
125
142
  }
126
143
 
@@ -197,7 +214,30 @@ export class LocalStorageFileSystemAdapter extends RecordFileSystemAdapter {
197
214
  }
198
215
  }
199
216
 
200
- export class ObjectFileSystemAdapter extends RecordFileSystemAdapter {}
217
+ export type ObjectFileSystem = { [k: string]: string | ObjectFileSystem };
218
+ export class ObjectFileSystemAdapter extends RecordFileSystemAdapter {
219
+ constructor(object: ObjectFileSystem) {
220
+ super(reduceObjectFileSystem(object));
221
+ }
222
+ }
223
+
224
+ function reduceObjectFileSystem(
225
+ object: ObjectFileSystem
226
+ ): Record<string, string> {
227
+ let level: Record<string, string> = {};
228
+
229
+ for (let [k, v] of Object.entries(object)) {
230
+ if (typeof v == "string") {
231
+ level[`/${k}`] = v;
232
+ } else {
233
+ for (let [k2, v2] of Object.entries(reduceObjectFileSystem(v))) {
234
+ level[`/${k}/${k2}`] = v2;
235
+ }
236
+ }
237
+ }
238
+
239
+ return level;
240
+ }
201
241
 
202
242
  export interface Tree {
203
243
  [k: string]: string | Tree;
package/src/fs_node.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { FileSystem, FileSystemAdapter, Stats } from "./fs";
2
+ import { copyFile, readdir, readFile, rm, stat, writeFile } from "fs/promises";
3
+ import { basename, join } from "path";
4
+
5
+ export class NodeFileSystem extends FileSystem {
6
+ constructor(cd: string = process.cwd()) {
7
+ super(new NodeFileSystemAdapter());
8
+ this.cd(cd);
9
+ }
10
+ }
11
+
12
+ /** Jiffies FileSystemAdapter using NodeJS' fs/promises. */
13
+ export class NodeFileSystemAdapter implements FileSystemAdapter {
14
+ async stat(path: string): Promise<Stats> {
15
+ const fsStat = await stat(path);
16
+ return {
17
+ name: basename(path),
18
+ isDirectory() {
19
+ return fsStat.isDirectory();
20
+ },
21
+ isFile() {
22
+ return !fsStat.isDirectory();
23
+ },
24
+ };
25
+ }
26
+ readdir(path: string): Promise<string[]> {
27
+ return readdir(path);
28
+ }
29
+ async scandir(path: string): Promise<Stats[]> {
30
+ return Promise.all(
31
+ (await this.readdir(path)).map((name) => this.stat(join(path, name)))
32
+ );
33
+ }
34
+ copyFile(from: string, to: string): Promise<void> {
35
+ return copyFile(from, to);
36
+ }
37
+ readFile(path: string): Promise<string> {
38
+ return readFile(path, "utf-8");
39
+ }
40
+ writeFile(path: string, contents: string): Promise<void> {
41
+ return writeFile(path, contents, "utf-8");
42
+ }
43
+ rm(path: string): Promise<void> {
44
+ return rm(path);
45
+ }
46
+ }
package/src/transpile.mjs CHANGED
@@ -14,7 +14,7 @@ export async function transpile(
14
14
  /** @type string */ url,
15
15
  /** @type {() => Promise<{toString(): string}>} */ get
16
16
  ) {
17
- if (!tsmap.has(url) || true) {
17
+ if (!tsmap.has(url)) {
18
18
  const source = ts.transpile(
19
19
  (await get()).toString(),
20
20
  compilerOptions,