fs-fixture 1.2.0 → 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/README.md CHANGED
@@ -1,45 +1,62 @@
1
- # fs-fixture
1
+ <p align="center">
2
+ <img width="160" src=".github/logo.webp">
3
+ </p>
4
+ <h1 align="center">
5
+ <sup>fs-fixture</sup>
6
+ <br>
7
+ <a href="https://npm.im/fs-fixture"><img src="https://badgen.net/npm/v/fs-fixture"></a> <a href="https://npm.im/fs-fixture"><img src="https://badgen.net/npm/dm/fs-fixture"></a>
8
+ </h1>
2
9
 
3
- Easily create test fixtures at a temporary file-system path.
10
+ Simple API to create disposable test fixtures on disk.
4
11
 
5
- <sub>Support this project by ⭐️ starring and sharing it. [Follow me](https://github.com/privatenumber) to see what other cool projects I'm working on! ❤️</sub>
6
-
7
- ## Usage
8
-
9
- ### JSON input
10
-
11
- Pass in an object representing the test fixture.
12
+ Tiny (`560 B` gzipped) and no dependencies!
12
13
 
14
+ ### Example
13
15
  ```ts
16
+ import fs from 'fs/promises'
14
17
  import { createFixture } from 'fs-fixture'
15
18
 
16
- test('my test using json fixture', async () => {
17
- // Pass in a JSON representing the test fixture
18
- const fixture = await createFixture({
19
- // Nested directory syntax
20
- directoryA: {
21
- directoryB: {
22
- fileNameA: 'fileContent'
23
- }
24
- },
19
+ const fixture = await createFixture({
20
+ 'dir-a': {
21
+ 'file-b': 'hello world'
22
+ }
23
+ })
24
+
25
+ const content = await fs.readFile(fixture.getPath('dir-a/file-b'))
26
+ console.log(content)
27
+ ```
25
28
 
26
- // Directory path syntax - Same as above
27
- 'directoryA/directoryB/fileNameB': 'fileContent'
28
- })
29
+ <p align="center">
30
+ <a href="https://github.com/sponsors/privatenumber/sponsorships?tier_id=398771"><img width="412" src="https://raw.githubusercontent.com/privatenumber/sponsors/master/banners/assets/donate.webp"></a>
31
+ <a href="https://github.com/sponsors/privatenumber/sponsorships?tier_id=397608"><img width="412" src="https://raw.githubusercontent.com/privatenumber/sponsors/master/banners/assets/sponsor.webp"></a>
32
+ </p>
33
+ <p align="center"><sup><i>Already a sponsor?</i> Join the discussion in the <a href="https://github.com/pvtnbr/fs-fixture">Development repo</a>!</sup></p>
29
34
 
30
- /*
31
- Your test code here...
35
+ ## Usage
32
36
 
33
- // Log fixture path
34
- console.log(fixture.path)
37
+ Pass in an object representing the file structure:
35
38
 
36
- // Check if relative path exists
37
- console.log(await fixture.exists('./file'))
38
- */
39
+ ```ts
40
+ import { createFixture } from 'fs-fixture'
39
41
 
40
- // Cleanup fixture
41
- await fixture.rm()
42
+ const fixture = await createFixture({
43
+ // Nested directory syntax
44
+ 'dir-a': {
45
+ 'dir-b': {
46
+ 'file-a.txt': 'hello world',
47
+ 'file-b.txt': ({ fixturePath }) => `Fixture path: ${fixturePath}`
48
+ }
49
+ },
50
+
51
+ // Alternatively, use the directory path syntax - Same as above
52
+ 'dir-a/dir-b/file-b.txt': 'goodbye world'
42
53
  })
54
+
55
+ // Interact with the fixture
56
+ console.log(fixture.path)
57
+
58
+ // Cleanup fixture
59
+ await fixture.rm()
43
60
  ```
44
61
 
45
62
  ### Template path input
@@ -47,15 +64,23 @@ test('my test using json fixture', async () => {
47
64
  Pass in a path to a test fixture template directory to make a copy of it.
48
65
 
49
66
  ```ts
50
- test('my test using template path', async () => {
51
- // Pass in a path to a fixture template path, and it will make a copy of it
52
- const fixture = await createFixture('./fixtures/template-a')
67
+ // Pass in a path to a fixture template path, and it will make a copy of it
68
+ const fixture = await createFixture('./fixtures/template-a')
53
69
 
54
- /* Your test code here... */
70
+ /* Your test code here... */
55
71
 
56
- // Cleanup fixture
57
- await fixture.rm()
58
- })
72
+ // Cleanup fixture
73
+ await fixture.rm()
74
+ ```
75
+
76
+ ### `using` keyword (Explicit Resource Management)
77
+
78
+ [TypeScript 5.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html) supports the [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management) feature, which allows you to instantiate the fixture via `using`. When the fixture is declared this way, it gets automatically cleaned up when exiting the scope.
79
+
80
+ ```ts
81
+ await using fixture = await createFixture({ file: 'hello' })
82
+
83
+ // No need to run fixture.rm()
59
84
  ```
60
85
 
61
86
  ## API
@@ -86,13 +111,18 @@ class FsFixture {
86
111
  /**
87
112
  Path to the fixture directory.
88
113
  */
89
- path: string
114
+ readonly path: string
90
115
 
91
116
  /**
92
117
  Create a Fixture instance from a path. Does not create the fixture directory.
93
118
  */
94
119
  constructor(fixturePath: string)
95
120
 
121
+ /**
122
+ Get the full path to a subpath in the fixture directory.
123
+ */
124
+ getPath(subpath: string): string
125
+
96
126
  /**
97
127
  Check if the fixture exists. Pass in a subpath to check if it exists.
98
128
  */
@@ -111,7 +141,7 @@ class FsFixture {
111
141
  /**
112
142
  Create a JSON file in the fixture directory.
113
143
  */
114
- writeJson(filePath: string, json: any): Promise<void>
144
+ writeJson(filePath: string, json: unknown): Promise<void>
115
145
 
116
146
  /**
117
147
  Read a file from the fixture directory.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var i=require("fs"),c=require("path"),l=require("os");function o(e){return e&&typeof e=="object"&&"default"in e?e:{default:e}}var p=o(i),a=o(c),d=o(l);class h{path;constructor(t){this.path=t}exists(t=""){return i.promises.access(a.default.join(this.path,t)).then(()=>!0,()=>!1)}rm(t=""){return i.promises.rm(a.default.join(this.path,t),{recursive:!0,force:!0})}writeFile(t,r){return i.promises.writeFile(a.default.join(this.path,t),r)}writeJson(t,r){return this.writeFile(t,JSON.stringify(r,null,2))}readFile(t,r){return i.promises.readFile(a.default.join(this.path,t),r)}}const m=p.default.realpathSync(d.default.tmpdir()),y=`fs-fixture-${Date.now()}`;let u=0;function w(){return u+=1,u}const{hasOwnProperty:j}=Object.prototype,v=(e,t)=>j.call(e,t);function f(e,t){const r=[];for(const s in e){if(!v(e,s))continue;const n=e[s];typeof n=="string"?r.push({path:a.default.join(t,s),content:n}):r.push(...f(n,a.default.join(t,s)))}return r}async function F(e){const t=a.default.join(m,`${y}-${w()}`);return await i.promises.mkdir(t,{recursive:!0}),e&&(typeof e=="string"?await i.promises.cp(e,t,{recursive:!0}):typeof e=="object"&&await Promise.all(f(e,t).map(async r=>{await i.promises.mkdir(a.default.dirname(r.path),{recursive:!0}),await i.promises.writeFile(r.path,r.content)}))),new h(t)}exports.createFixture=F;
1
+ "use strict";var i=require("fs/promises"),a=require("path"),l=require("fs"),f=require("os");typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class h{path;constructor(t){this.path=t}getPath(t){return a.join(this.path,t)}exists(t=""){return i.access(this.getPath(t)).then(()=>!0,()=>!1)}rm(t=""){return i.rm(this.getPath(t),{recursive:!0,force:!0})}writeFile(t,e){return i.writeFile(this.getPath(t),e)}writeJson(t,e){return this.writeFile(t,JSON.stringify(e,null,2))}readFile(t,e){return i.readFile(this.getPath(t),e)}async[Symbol.asyncDispose](){await this.rm()}}const p=l.realpathSync(f.tmpdir()),y=`fs-fixture-${Date.now()}`;let c=0;const m=()=>(c+=1,c),u=(r,t,e)=>{const o=[];for(const n in r){if(!Object.hasOwn(r,n))continue;let s=r[n];typeof s=="function"&&(s=s(e)),typeof s=="string"?o.push({path:a.join(t,n),content:s}):o.push(...u(s,a.join(t,n),e))}return o},w=async r=>{const t=a.join(p,`${y}-${m()}/`);return await i.mkdir(t,{recursive:!0}),r&&(typeof r=="string"?await i.cp(r,t,{recursive:!0}):typeof r=="object"&&await Promise.all(u(r,t,{fixturePath:t}).map(async e=>{await i.mkdir(a.dirname(e.path),{recursive:!0}),await i.writeFile(e.path,e.content)}))),new h(t)};exports.createFixture=w;
package/dist/index.d.cts CHANGED
@@ -2,12 +2,16 @@ declare class FsFixture {
2
2
  /**
3
3
  Path to the fixture directory.
4
4
  */
5
- path: string;
5
+ readonly path: string;
6
6
  /**
7
7
  Create a Fixture instance from a path. Does not create the fixture directory.
8
8
  */
9
9
  constructor(fixturePath: string);
10
10
  /**
11
+ Get the full path to a subpath in the fixture directory.
12
+ */
13
+ getPath(subpath: string): string;
14
+ /**
11
15
  Check if the fixture exists. Pass in a subpath to check if it exists.
12
16
  */
13
17
  exists(subpath?: string): Promise<boolean>;
@@ -22,16 +26,25 @@ declare class FsFixture {
22
26
  /**
23
27
  Create a JSON file in the fixture directory.
24
28
  */
25
- writeJson(filePath: string, json: any): Promise<void>;
29
+ writeJson(filePath: string, json: unknown): Promise<void>;
26
30
  /**
27
31
  Read a file from the fixture directory.
28
32
  */
29
- readFile(filePath: string, encoding?: BufferEncoding): Promise<string | Buffer>;
33
+ readFile(filePath: string, encoding?: null): Promise<Buffer>;
34
+ readFile(filePath: string, encoding: BufferEncoding): Promise<string>;
35
+ /**
36
+ * Resource management cleanup
37
+ * https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html
38
+ */
39
+ [Symbol.asyncDispose](): Promise<void>;
30
40
  }
31
41
 
42
+ type Api = {
43
+ fixturePath: string;
44
+ };
32
45
  type FileTree = {
33
- [path: string]: string | FileTree;
46
+ [path: string]: string | FileTree | ((api: Api) => string);
34
47
  };
35
- declare function createFixture(source?: string | FileTree): Promise<FsFixture>;
48
+ declare const createFixture: (source?: string | FileTree) => Promise<FsFixture>;
36
49
 
37
- export { FileTree, FsFixture, createFixture };
50
+ export { type FileTree, FsFixture, createFixture };
package/dist/index.d.mts CHANGED
@@ -2,12 +2,16 @@ declare class FsFixture {
2
2
  /**
3
3
  Path to the fixture directory.
4
4
  */
5
- path: string;
5
+ readonly path: string;
6
6
  /**
7
7
  Create a Fixture instance from a path. Does not create the fixture directory.
8
8
  */
9
9
  constructor(fixturePath: string);
10
10
  /**
11
+ Get the full path to a subpath in the fixture directory.
12
+ */
13
+ getPath(subpath: string): string;
14
+ /**
11
15
  Check if the fixture exists. Pass in a subpath to check if it exists.
12
16
  */
13
17
  exists(subpath?: string): Promise<boolean>;
@@ -22,16 +26,25 @@ declare class FsFixture {
22
26
  /**
23
27
  Create a JSON file in the fixture directory.
24
28
  */
25
- writeJson(filePath: string, json: any): Promise<void>;
29
+ writeJson(filePath: string, json: unknown): Promise<void>;
26
30
  /**
27
31
  Read a file from the fixture directory.
28
32
  */
29
- readFile(filePath: string, encoding?: BufferEncoding): Promise<string | Buffer>;
33
+ readFile(filePath: string, encoding?: null): Promise<Buffer>;
34
+ readFile(filePath: string, encoding: BufferEncoding): Promise<string>;
35
+ /**
36
+ * Resource management cleanup
37
+ * https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html
38
+ */
39
+ [Symbol.asyncDispose](): Promise<void>;
30
40
  }
31
41
 
42
+ type Api = {
43
+ fixturePath: string;
44
+ };
32
45
  type FileTree = {
33
- [path: string]: string | FileTree;
46
+ [path: string]: string | FileTree | ((api: Api) => string);
34
47
  };
35
- declare function createFixture(source?: string | FileTree): Promise<FsFixture>;
48
+ declare const createFixture: (source?: string | FileTree) => Promise<FsFixture>;
36
49
 
37
- export { FileTree, FsFixture, createFixture };
50
+ export { type FileTree, FsFixture, createFixture };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import p,{promises as i}from"fs";import n from"path";import u from"os";class h{path;constructor(t){this.path=t}exists(t=""){return i.access(n.join(this.path,t)).then(()=>!0,()=>!1)}rm(t=""){return i.rm(n.join(this.path,t),{recursive:!0,force:!0})}writeFile(t,e){return i.writeFile(n.join(this.path,t),e)}writeJson(t,e){return this.writeFile(t,JSON.stringify(e,null,2))}readFile(t,e){return i.readFile(n.join(this.path,t),e)}}const f=p.realpathSync(u.tmpdir()),l=`fs-fixture-${Date.now()}`;let a=0;function m(){return a+=1,a}const{hasOwnProperty:w}=Object.prototype,y=(r,t)=>w.call(r,t);function c(r,t){const e=[];for(const o in r){if(!y(r,o))continue;const s=r[o];typeof s=="string"?e.push({path:n.join(t,o),content:s}):e.push(...c(s,n.join(t,o)))}return e}async function F(r){const t=n.join(f,`${l}-${m()}`);return await i.mkdir(t,{recursive:!0}),r&&(typeof r=="string"?await i.cp(r,t,{recursive:!0}):typeof r=="object"&&await Promise.all(c(r,t).map(async e=>{await i.mkdir(n.dirname(e.path),{recursive:!0}),await i.writeFile(e.path,e.content)}))),new h(t)}export{F as createFixture};
1
+ import i from"fs/promises";import a from"path";import l from"fs";import p from"os";typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class u{path;constructor(t){this.path=t}getPath(t){return a.join(this.path,t)}exists(t=""){return i.access(this.getPath(t)).then(()=>!0,()=>!1)}rm(t=""){return i.rm(this.getPath(t),{recursive:!0,force:!0})}writeFile(t,e){return i.writeFile(this.getPath(t),e)}writeJson(t,e){return this.writeFile(t,JSON.stringify(e,null,2))}readFile(t,e){return i.readFile(this.getPath(t),e)}async[Symbol.asyncDispose](){await this.rm()}}const h=l.realpathSync(p.tmpdir()),m=`fs-fixture-${Date.now()}`;let c=0;const y=()=>(c+=1,c),f=(r,t,e)=>{const o=[];for(const n in r){if(!Object.hasOwn(r,n))continue;let s=r[n];typeof s=="function"&&(s=s(e)),typeof s=="string"?o.push({path:a.join(t,n),content:s}):o.push(...f(s,a.join(t,n),e))}return o},w=async r=>{const t=a.join(h,`${m}-${y()}/`);return await i.mkdir(t,{recursive:!0}),r&&(typeof r=="string"?await i.cp(r,t,{recursive:!0}):typeof r=="object"&&await Promise.all(f(r,t,{fixturePath:t}).map(async e=>{await i.mkdir(a.dirname(e.path),{recursive:!0}),await i.writeFile(e.path,e.content)}))),new u(t)};export{w as createFixture};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fs-fixture",
3
- "version": "1.2.0",
3
+ "version": "2.1.0",
4
4
  "description": "Easily create test fixtures at a temporary file-system path",
5
5
  "keywords": [
6
6
  "test",
@@ -43,6 +43,6 @@
43
43
  }
44
44
  },
45
45
  "engines": {
46
- "node": ">=16.7.0"
46
+ "node": ">=18.0.0"
47
47
  }
48
48
  }