fs-fixture 2.8.1 โ 2.9.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 +154 -123
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +149 -33
- package/dist/index.d.mts +149 -33
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,23 +7,51 @@
|
|
|
7
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
8
|
</h1>
|
|
9
9
|
|
|
10
|
-
Simple API to create disposable test fixtures on disk.
|
|
10
|
+
Simple API to create disposable test fixtures on disk. Tiny (`1.1 kB` gzipped) with zero dependencies!
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
### Features
|
|
13
|
+
- ๐ Create files & directories from simple objects
|
|
14
|
+
- ๐งน Automatic cleanup with `using` keyword
|
|
15
|
+
- ๐ Built-in JSON read/write support
|
|
16
|
+
- ๐ Symlink support
|
|
17
|
+
- ๐พ Binary file support with Buffers
|
|
18
|
+
- ๐ฏ TypeScript-first with full type safety
|
|
19
|
+
- ๐ File methods inherit types directly from Node.js `fs` module
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install fs-fixture
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
13
28
|
|
|
14
|
-
### Example
|
|
15
29
|
```ts
|
|
16
|
-
import fs from 'node:fs/promises'
|
|
17
30
|
import { createFixture } from 'fs-fixture'
|
|
18
31
|
|
|
32
|
+
// Create a temporary fixture
|
|
19
33
|
const fixture = await createFixture({
|
|
20
|
-
'
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
'package.json': JSON.stringify({ name: 'my-app' }),
|
|
35
|
+
'src/index.js': 'console.log("Hello world")'
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Read files
|
|
39
|
+
const content = await fixture.readFile('src/index.js', 'utf8')
|
|
40
|
+
|
|
41
|
+
// Cleanup when done
|
|
42
|
+
await fixture.rm()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Auto cleanup with `using` keyword
|
|
46
|
+
|
|
47
|
+
Uses TypeScript 5.2+ [Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html) for automatic cleanup:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
await using fixture = await createFixture({
|
|
51
|
+
'config.json': '{ "setting": true }'
|
|
23
52
|
})
|
|
24
53
|
|
|
25
|
-
|
|
26
|
-
console.log(content)
|
|
54
|
+
// Fixture is automatically cleaned up when exiting scope
|
|
27
55
|
```
|
|
28
56
|
|
|
29
57
|
<p align="center">
|
|
@@ -34,163 +62,166 @@ console.log(content)
|
|
|
34
62
|
|
|
35
63
|
## Usage
|
|
36
64
|
|
|
37
|
-
|
|
65
|
+
### Creating fixtures
|
|
38
66
|
|
|
67
|
+
**From an object:**
|
|
39
68
|
```ts
|
|
40
|
-
import { createFixture } from 'fs-fixture'
|
|
41
|
-
|
|
42
69
|
const fixture = await createFixture({
|
|
43
|
-
|
|
44
|
-
'
|
|
45
|
-
|
|
46
|
-
'
|
|
47
|
-
|
|
48
|
-
'symlink-c': ({ symlink }) => symlink('../file-a.txt')
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
// Alternatively, use the directory path syntax - Same as above
|
|
53
|
-
'dir-a/dir-b/file-b.txt': 'goodbye world'
|
|
70
|
+
'package.json': '{ "name": "test" }',
|
|
71
|
+
'src/index.js': 'export default () => {}',
|
|
72
|
+
'src/utils': {
|
|
73
|
+
'helper.js': 'export const help = () => {}'
|
|
74
|
+
}
|
|
54
75
|
})
|
|
76
|
+
```
|
|
55
77
|
|
|
56
|
-
|
|
57
|
-
|
|
78
|
+
**From a template directory:**
|
|
79
|
+
```ts
|
|
80
|
+
// Copies an existing directory structure
|
|
81
|
+
const fixture = await createFixture('./test-templates/basic')
|
|
82
|
+
```
|
|
58
83
|
|
|
59
|
-
|
|
60
|
-
|
|
84
|
+
**Empty fixture:**
|
|
85
|
+
```ts
|
|
86
|
+
// Create an empty temporary directory
|
|
87
|
+
const fixture = await createFixture()
|
|
61
88
|
```
|
|
62
89
|
|
|
63
|
-
###
|
|
90
|
+
### Working with files
|
|
64
91
|
|
|
65
|
-
|
|
92
|
+
File methods (`readFile`, `writeFile`, `readdir`) inherit their type signatures directly from Node.js `fs/promises`, preserving all overloads and type narrowing behavior.
|
|
66
93
|
|
|
94
|
+
**Read files:**
|
|
67
95
|
```ts
|
|
68
|
-
//
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
/* Your test code here... */
|
|
96
|
+
// Read as string (type: Promise<string>)
|
|
97
|
+
const text = await fixture.readFile('config.txt', 'utf8')
|
|
72
98
|
|
|
73
|
-
//
|
|
74
|
-
await fixture.
|
|
99
|
+
// Read as buffer (type: Promise<Buffer>)
|
|
100
|
+
const binary = await fixture.readFile('image.png')
|
|
75
101
|
```
|
|
76
102
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
**Write files:**
|
|
104
|
+
```ts
|
|
105
|
+
await fixture.writeFile('output.txt', 'Hello world')
|
|
106
|
+
await fixture.writeFile('data.bin', Buffer.from([0x89, 0x50]))
|
|
107
|
+
```
|
|
80
108
|
|
|
109
|
+
**JSON operations:**
|
|
81
110
|
```ts
|
|
82
|
-
|
|
111
|
+
// Write JSON with formatting
|
|
112
|
+
await fixture.writeJson('config.json', { port: 3000 })
|
|
83
113
|
|
|
84
|
-
//
|
|
114
|
+
// Read and parse JSON with type safety
|
|
115
|
+
type Config = { port: number }
|
|
116
|
+
const config = await fixture.readJson<Config>('config.json')
|
|
85
117
|
```
|
|
86
118
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
### createFixture(source, options)
|
|
119
|
+
### Working with directories
|
|
90
120
|
|
|
91
|
-
|
|
121
|
+
```ts
|
|
122
|
+
// Create directories
|
|
123
|
+
await fixture.mkdir('nested/folders')
|
|
92
124
|
|
|
93
|
-
|
|
94
|
-
|
|
125
|
+
// List directory contents
|
|
126
|
+
const files = await fixture.readdir('src')
|
|
95
127
|
|
|
96
|
-
|
|
128
|
+
// Copy files into fixture
|
|
129
|
+
await fixture.cp('/path/to/file.txt', 'copied-file.txt')
|
|
97
130
|
|
|
131
|
+
// Check if path exists
|
|
132
|
+
if (await fixture.exists('optional-file.txt')) {
|
|
133
|
+
// ...
|
|
134
|
+
}
|
|
135
|
+
```
|
|
98
136
|
|
|
99
|
-
|
|
137
|
+
### Advanced features
|
|
100
138
|
|
|
101
|
-
|
|
139
|
+
**Dynamic content with functions:**
|
|
140
|
+
```ts
|
|
141
|
+
const fixture = await createFixture({
|
|
142
|
+
'target.txt': 'original file',
|
|
143
|
+
'info.txt': ({ fixturePath }) => `Created at: ${fixturePath}`,
|
|
144
|
+
'link.txt': ({ symlink }) => symlink('./target.txt')
|
|
145
|
+
})
|
|
146
|
+
```
|
|
102
147
|
|
|
103
|
-
|
|
148
|
+
**Binary files:**
|
|
149
|
+
```ts
|
|
150
|
+
const fixture = await createFixture({
|
|
151
|
+
'image.png': Buffer.from(imageData),
|
|
152
|
+
'generated.bin': () => Buffer.from('dynamic binary content')
|
|
153
|
+
})
|
|
154
|
+
```
|
|
104
155
|
|
|
105
|
-
|
|
156
|
+
**Path syntax:**
|
|
157
|
+
```ts
|
|
158
|
+
const fixture = await createFixture({
|
|
159
|
+
// Nested object syntax
|
|
160
|
+
src: {
|
|
161
|
+
utils: {
|
|
162
|
+
'helper.js': 'export const help = () => {}'
|
|
163
|
+
}
|
|
164
|
+
},
|
|
106
165
|
|
|
107
|
-
|
|
166
|
+
// Or path syntax (creates same structure)
|
|
167
|
+
'src/utils/helper.js': 'export const help = () => {}'
|
|
168
|
+
})
|
|
169
|
+
```
|
|
108
170
|
|
|
171
|
+
## API
|
|
109
172
|
|
|
110
|
-
|
|
173
|
+
### `createFixture(source?, options?)`
|
|
111
174
|
|
|
112
|
-
|
|
175
|
+
Creates a temporary fixture directory and returns a `FsFixture` instance.
|
|
113
176
|
|
|
114
|
-
|
|
177
|
+
**Parameters:**
|
|
178
|
+
- `source` (optional): String path to template directory, or `FileTree` object defining the structure
|
|
179
|
+
- `options.tempDir` (optional): Custom temp directory. Defaults to `os.tmpdir()`
|
|
180
|
+
- `options.templateFilter` (optional): Filter function when copying from template directory
|
|
115
181
|
|
|
116
|
-
|
|
117
|
-
#### FileTree
|
|
182
|
+
**Returns:** `Promise<FsFixture>`
|
|
118
183
|
|
|
119
184
|
```ts
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
185
|
+
const fixture = await createFixture()
|
|
186
|
+
const fixture = await createFixture({ 'file.txt': 'content' })
|
|
187
|
+
const fixture = await createFixture('./template-dir')
|
|
188
|
+
const fixture = await createFixture({}, { tempDir: './custom-temp' })
|
|
189
|
+
```
|
|
123
190
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
191
|
+
### `FsFixture` Methods
|
|
192
|
+
|
|
193
|
+
| Method | Description |
|
|
194
|
+
|--------|-------------|
|
|
195
|
+
| `fixture.path` | Absolute path to the fixture directory |
|
|
196
|
+
| `getPath(...paths)` | Get absolute path to file/directory in fixture |
|
|
197
|
+
| `exists(path?)` | Check if file/directory exists |
|
|
198
|
+
| `rm(path?)` | Delete file/directory (or entire fixture if no path) |
|
|
199
|
+
| `readFile(path, encoding?)` | Read file as string or Buffer |
|
|
200
|
+
| `writeFile(path, content)` | Write string or Buffer to file |
|
|
201
|
+
| `readJson<T>(path)` | Read and parse JSON file |
|
|
202
|
+
| `writeJson(path, data, space?)` | Write JSON with optional formatting |
|
|
203
|
+
| `readdir(path, options?)` | List directory contents |
|
|
204
|
+
| `mkdir(path)` | Create directory (recursive) |
|
|
205
|
+
| `cp(source, dest?)` | Copy file/directory into fixture |
|
|
127
206
|
|
|
128
|
-
|
|
129
|
-
filePath: string
|
|
207
|
+
### Types
|
|
130
208
|
|
|
131
|
-
|
|
132
|
-
|
|
209
|
+
<details>
|
|
210
|
+
<summary><strong>FileTree</strong></summary>
|
|
133
211
|
|
|
134
|
-
|
|
135
|
-
|
|
212
|
+
```ts
|
|
213
|
+
type FileTree = {
|
|
214
|
+
[path: string]: string | Buffer | FileTree | ((api: Api) => string | Buffer | Symlink)
|
|
136
215
|
}
|
|
137
|
-
```
|
|
138
216
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
Path to the fixture directory.
|
|
145
|
-
*/
|
|
146
|
-
readonly path: string
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
Create a Fixture instance from a path. Does not create the fixture directory.
|
|
150
|
-
*/
|
|
151
|
-
constructor(fixturePath: string)
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
Get the full path to a subpath in the fixture directory.
|
|
155
|
-
*/
|
|
156
|
-
getPath(...subpaths: string[]): string
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
Check if the fixture exists. Pass in a subpath to check if it exists.
|
|
160
|
-
*/
|
|
161
|
-
exists(subpath?: string): Promise<boolean>
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
Delete the fixture directory. Pass in a subpath to delete it.
|
|
165
|
-
*/
|
|
166
|
-
rm(subpath?: string): Promise<void>
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
Copy a path into the fixture directory.
|
|
170
|
-
*/
|
|
171
|
-
cp(sourcePath: string, destinationSubpath?: string): Promise<void>
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
Create a new folder in the fixture directory.
|
|
175
|
-
*/
|
|
176
|
-
mkdir(folderPath: string): Promise<void>
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
Create a file in the fixture directory.
|
|
180
|
-
*/
|
|
181
|
-
writeFile(filePath: string, content: string): Promise<void>
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
Create a JSON file in the fixture directory.
|
|
185
|
-
*/
|
|
186
|
-
writeJson(filePath: string, json: unknown): Promise<void>
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
Read a file from the fixture directory.
|
|
190
|
-
*/
|
|
191
|
-
readFile(filePath: string, encoding?: BufferEncoding): Promise<string | Buffer>
|
|
217
|
+
type Api = {
|
|
218
|
+
fixturePath: string // Fixture root path
|
|
219
|
+
filePath: string // Current file path
|
|
220
|
+
getPath: (...paths: string[]) => string // Get path from fixture root
|
|
221
|
+
symlink: (target: string) => Symlink // Create a symlink
|
|
192
222
|
}
|
|
193
223
|
```
|
|
224
|
+
</details>
|
|
194
225
|
|
|
195
226
|
## Related
|
|
196
227
|
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var a=(i,e)=>v(i,"name",{value:e,configurable:!0});var n=require("node:fs/promises"),c=require("node:path"),F=require("node:fs"),P=require("node:os");typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class b{static{a(this,"FsFixture")}path;constructor(e){this.path=e}getPath(...e){return c.join(this.path,...e)}exists(e=""){return n.access(this.getPath(e)).then(()=>!0,()=>!1)}rm(e=""){return n.rm(this.getPath(e),{recursive:!0,force:!0})}cp(e,t,s){return t?t.endsWith(c.sep)&&(t+=c.basename(e)):t=c.basename(e),n.cp(e,this.getPath(t),s)}mkdir(e){return n.mkdir(this.getPath(e),{recursive:!0})}readFile=a(((e,t)=>n.readFile(this.getPath(e),t)),"readFile");readdir=a(((e,t)=>n.readdir(this.getPath(e||""),t)),"readdir");writeFile=a(((e,t,...s)=>n.writeFile(this.getPath(e),t,...s)),"writeFile");async readJson(e){const t=await this.readFile(e,"utf8");return JSON.parse(t)}writeJson(e,t,s=2){return this.writeFile(e,JSON.stringify(t,null,s))}async[Symbol.asyncDispose](){await this.rm()}}const k=F.realpathSync(P.tmpdir()),D=`fs-fixture-${Date.now()}-${process.pid}`;let f=0;const j=a(()=>(f+=1,f),"getId");class p{static{a(this,"PathBase")}constructor(e){this.path=e}}class y extends p{static{a(this,"Directory")}}class m extends p{static{a(this,"File")}constructor(e,t){super(e),this.content=t}}class h extends p{static{a(this,"Symlink")}constructor(e,t,s){super(s??""),this.target=e,this.type=t}}const d=a((i,e,t)=>{const s=[];for(const u in i){if(!Object.hasOwn(i,u))continue;const r=c.join(e,u);let o=i[u];if(typeof o=="function"){const w=Object.assign(Object.create(t),{filePath:r}),l=o(w);if(l instanceof h){const g=new h(l.target,l.type,r);s.push(g);continue}else o=l}typeof o=="string"||Buffer.isBuffer(o)?s.push(new m(r,o)):s.push(new y(r),...d(o,r,t))}return s},"flattenFileTree"),x=a(async(i,e)=>{const t=e?.tempDir?c.resolve(e.tempDir):k,s=c.join(t,`${D}-${j()}/`);if(await n.mkdir(s,{recursive:!0}),i){if(typeof i=="string")await n.cp(i,s,{recursive:!0,filter:e?.templateFilter});else if(typeof i=="object"){const u={fixturePath:s,getPath:a((...r)=>c.join(s,...r),"getPath"),symlink:a((r,o)=>new h(r,o),"symlink")};await Promise.all(d(i,s,u).map(async r=>{r instanceof y?await n.mkdir(r.path,{recursive:!0}):r instanceof h?(await n.mkdir(c.dirname(r.path),{recursive:!0}),await n.symlink(r.target,r.path,r.type)):r instanceof m&&(await n.mkdir(c.dirname(r.path),{recursive:!0}),await n.writeFile(r.path,r.content))}))}}return new b(s)},"createFixture");exports.createFixture=x;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,47 +1,128 @@
|
|
|
1
1
|
import { CopyOptions } from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
2
3
|
|
|
3
4
|
declare class FsFixture {
|
|
4
5
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
* Path to the fixture directory.
|
|
7
|
+
*/
|
|
7
8
|
readonly path: string;
|
|
8
9
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
* Create a Fixture instance from a path. Does not create the fixture directory.
|
|
11
|
+
*
|
|
12
|
+
* @param fixturePath - The path to the fixture directory
|
|
13
|
+
*/
|
|
11
14
|
constructor(fixturePath: string);
|
|
12
15
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
* Get the full path to a subpath in the fixture directory.
|
|
17
|
+
*
|
|
18
|
+
* @param subpaths - Path segments to join with the fixture directory
|
|
19
|
+
* @returns The absolute path to the subpath
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* fixture.getPath('dir', 'file.txt')
|
|
24
|
+
* // => '/tmp/fs-fixture-123/dir/file.txt'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
15
27
|
getPath(...subpaths: string[]): string;
|
|
16
28
|
/**
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
* Check if the fixture exists. Pass in a subpath to check if it exists.
|
|
30
|
+
*
|
|
31
|
+
* @param subpath - Optional subpath to check within the fixture directory
|
|
32
|
+
* @returns Promise resolving to true if the path exists, false otherwise
|
|
33
|
+
*/
|
|
19
34
|
exists(subpath?: string): Promise<boolean>;
|
|
20
35
|
/**
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
* Delete the fixture directory or a subpath within it.
|
|
37
|
+
*
|
|
38
|
+
* @param subpath - Optional subpath to delete within the fixture directory.
|
|
39
|
+
* Defaults to deleting the entire fixture.
|
|
40
|
+
* @returns Promise that resolves when deletion is complete
|
|
41
|
+
*/
|
|
23
42
|
rm(subpath?: string): Promise<void>;
|
|
24
43
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
* Copy a file or directory into the fixture directory.
|
|
45
|
+
*
|
|
46
|
+
* @param sourcePath - The source path to copy from
|
|
47
|
+
* @param destinationSubpath - Optional destination path within the fixture.
|
|
48
|
+
* If omitted, uses the basename of sourcePath.
|
|
49
|
+
* If ends with path separator, appends basename of sourcePath.
|
|
50
|
+
* @param options - Copy options (e.g., recursive, filter)
|
|
51
|
+
* @returns Promise that resolves when copy is complete
|
|
52
|
+
*/
|
|
27
53
|
cp(sourcePath: string, destinationSubpath?: string, options?: CopyOptions): Promise<void>;
|
|
28
54
|
/**
|
|
29
|
-
|
|
30
|
-
|
|
55
|
+
* Create a new folder in the fixture directory (including parent directories).
|
|
56
|
+
*
|
|
57
|
+
* @param folderPath - The folder path to create within the fixture
|
|
58
|
+
* @returns Promise that resolves when directory is created
|
|
59
|
+
*/
|
|
31
60
|
mkdir(folderPath: string): Promise<string | undefined>;
|
|
32
61
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
* Read a file from the fixture directory.
|
|
63
|
+
*
|
|
64
|
+
* @param filePath - The file path within the fixture to read
|
|
65
|
+
* @param options - Optional encoding or read options.
|
|
66
|
+
* When encoding is specified, returns a string; otherwise returns a Buffer.
|
|
67
|
+
* @returns Promise resolving to file contents as string or Buffer
|
|
68
|
+
*/
|
|
69
|
+
readFile: typeof fs.readFile;
|
|
36
70
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
* Read the contents of a directory in the fixture.
|
|
72
|
+
*
|
|
73
|
+
* @param directoryPath - The directory path within the fixture to read.
|
|
74
|
+
* Defaults to the fixture root when empty string is passed.
|
|
75
|
+
* @param options - Optional read directory options.
|
|
76
|
+
* Use `{ withFileTypes: true }` to get Dirent objects.
|
|
77
|
+
* @returns Promise resolving to array of file/directory names or Dirent objects
|
|
78
|
+
*/
|
|
79
|
+
readdir: typeof fs.readdir;
|
|
40
80
|
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
81
|
+
* Create or overwrite a file in the fixture directory.
|
|
82
|
+
*
|
|
83
|
+
* @param filePath - The file path within the fixture to write
|
|
84
|
+
* @param data - The content to write (string or Buffer)
|
|
85
|
+
* @param options - Optional encoding or write options
|
|
86
|
+
* @returns Promise that resolves when file is written
|
|
87
|
+
*/
|
|
88
|
+
writeFile: typeof fs.writeFile;
|
|
89
|
+
/**
|
|
90
|
+
* Read and parse a JSON file from the fixture directory.
|
|
91
|
+
*
|
|
92
|
+
* @param filePath - The JSON file path within the fixture to read
|
|
93
|
+
* @returns Promise resolving to the parsed JSON content
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* const data = await fixture.readJson<{ name: string }>('config.json')
|
|
98
|
+
* console.log(data.name) // Typed as string
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
readJson<T = unknown>(filePath: string): Promise<T>;
|
|
102
|
+
/**
|
|
103
|
+
* Create or overwrite a JSON file in the fixture directory.
|
|
104
|
+
*
|
|
105
|
+
* @param filePath - The JSON file path within the fixture to write
|
|
106
|
+
* @param json - The data to serialize as JSON
|
|
107
|
+
* @param space - Number of spaces or string to use for indentation. Defaults to 2.
|
|
108
|
+
* @returns Promise that resolves when file is written
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* // Default 2-space indentation
|
|
113
|
+
* await fixture.writeJson('config.json', { key: 'value' })
|
|
114
|
+
*
|
|
115
|
+
* // 4-space indentation
|
|
116
|
+
* await fixture.writeJson('config.json', { key: 'value' }, 4)
|
|
117
|
+
*
|
|
118
|
+
* // Tab indentation
|
|
119
|
+
* await fixture.writeJson('config.json', { key: 'value' }, '\t')
|
|
120
|
+
*
|
|
121
|
+
* // Minified (no formatting)
|
|
122
|
+
* await fixture.writeJson('config.json', { key: 'value' }, 0)
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
writeJson(filePath: string, json: unknown, space?: string | number): Promise<void>;
|
|
45
126
|
/**
|
|
46
127
|
* Resource management cleanup
|
|
47
128
|
* https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html
|
|
@@ -50,27 +131,32 @@ declare class FsFixture {
|
|
|
50
131
|
}
|
|
51
132
|
type FsFixtureType = FsFixture;
|
|
52
133
|
|
|
134
|
+
declare class PathBase {
|
|
135
|
+
readonly path: string;
|
|
136
|
+
constructor(path: string);
|
|
137
|
+
}
|
|
138
|
+
|
|
53
139
|
type SymlinkType = 'file' | 'dir' | 'junction';
|
|
54
|
-
declare class Symlink {
|
|
55
|
-
target: string;
|
|
56
|
-
type?: SymlinkType;
|
|
57
|
-
|
|
58
|
-
constructor(target: string, type?: SymlinkType);
|
|
140
|
+
declare class Symlink extends PathBase {
|
|
141
|
+
readonly target: string;
|
|
142
|
+
readonly type?: SymlinkType | undefined;
|
|
143
|
+
constructor(target: string, type?: SymlinkType | undefined, filePath?: string);
|
|
59
144
|
}
|
|
145
|
+
|
|
60
146
|
type ApiBase = {
|
|
61
147
|
fixturePath: string;
|
|
62
148
|
getPath(...subpaths: string[]): string;
|
|
63
149
|
symlink(targetPath: string,
|
|
64
150
|
/**
|
|
65
|
-
|
|
66
|
-
|
|
151
|
+
* Symlink type for Windows. Defaults to auto-detect by Node.
|
|
152
|
+
*/
|
|
67
153
|
type?: SymlinkType): Symlink;
|
|
68
154
|
};
|
|
69
155
|
type Api = ApiBase & {
|
|
70
156
|
filePath: string;
|
|
71
157
|
};
|
|
72
158
|
type FileTree = {
|
|
73
|
-
[path: string]: string | FileTree | ((api: Api) => string | Symlink);
|
|
159
|
+
[path: string]: string | Buffer | FileTree | ((api: Api) => string | Buffer | Symlink);
|
|
74
160
|
};
|
|
75
161
|
|
|
76
162
|
type FilterFunction = CopyOptions['filter'];
|
|
@@ -86,6 +172,36 @@ type CreateFixtureOptions = {
|
|
|
86
172
|
*/
|
|
87
173
|
templateFilter?: FilterFunction;
|
|
88
174
|
};
|
|
175
|
+
/**
|
|
176
|
+
* Create a temporary test fixture directory.
|
|
177
|
+
*
|
|
178
|
+
* @param source - Optional source to create the fixture from:
|
|
179
|
+
* - If omitted, creates an empty fixture directory
|
|
180
|
+
* - If a string, copies the directory at that path to the fixture
|
|
181
|
+
* - If a FileTree object, creates files and directories from the object structure
|
|
182
|
+
* @param options - Optional configuration for fixture creation
|
|
183
|
+
* @returns Promise resolving to an FsFixture instance
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* // Create empty fixture
|
|
188
|
+
* const fixture = await createFixture()
|
|
189
|
+
*
|
|
190
|
+
* // Create from object
|
|
191
|
+
* const fixture = await createFixture({
|
|
192
|
+
* 'file.txt': 'content',
|
|
193
|
+
* 'dir/nested.txt': 'nested content',
|
|
194
|
+
* 'binary.bin': Buffer.from('binary'),
|
|
195
|
+
* })
|
|
196
|
+
*
|
|
197
|
+
* // Create from template directory
|
|
198
|
+
* const fixture = await createFixture('./my-template')
|
|
199
|
+
*
|
|
200
|
+
* // Cleanup
|
|
201
|
+
* await fixture.rm()
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
89
204
|
declare const createFixture: (source?: string | FileTree, options?: CreateFixtureOptions) => Promise<FsFixture>;
|
|
90
205
|
|
|
91
|
-
export {
|
|
206
|
+
export { createFixture };
|
|
207
|
+
export type { CreateFixtureOptions, FileTree, FsFixtureType as FsFixture };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,47 +1,128 @@
|
|
|
1
1
|
import { CopyOptions } from 'node:fs';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
2
3
|
|
|
3
4
|
declare class FsFixture {
|
|
4
5
|
/**
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
* Path to the fixture directory.
|
|
7
|
+
*/
|
|
7
8
|
readonly path: string;
|
|
8
9
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
* Create a Fixture instance from a path. Does not create the fixture directory.
|
|
11
|
+
*
|
|
12
|
+
* @param fixturePath - The path to the fixture directory
|
|
13
|
+
*/
|
|
11
14
|
constructor(fixturePath: string);
|
|
12
15
|
/**
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
* Get the full path to a subpath in the fixture directory.
|
|
17
|
+
*
|
|
18
|
+
* @param subpaths - Path segments to join with the fixture directory
|
|
19
|
+
* @returns The absolute path to the subpath
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* fixture.getPath('dir', 'file.txt')
|
|
24
|
+
* // => '/tmp/fs-fixture-123/dir/file.txt'
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
15
27
|
getPath(...subpaths: string[]): string;
|
|
16
28
|
/**
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
* Check if the fixture exists. Pass in a subpath to check if it exists.
|
|
30
|
+
*
|
|
31
|
+
* @param subpath - Optional subpath to check within the fixture directory
|
|
32
|
+
* @returns Promise resolving to true if the path exists, false otherwise
|
|
33
|
+
*/
|
|
19
34
|
exists(subpath?: string): Promise<boolean>;
|
|
20
35
|
/**
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
* Delete the fixture directory or a subpath within it.
|
|
37
|
+
*
|
|
38
|
+
* @param subpath - Optional subpath to delete within the fixture directory.
|
|
39
|
+
* Defaults to deleting the entire fixture.
|
|
40
|
+
* @returns Promise that resolves when deletion is complete
|
|
41
|
+
*/
|
|
23
42
|
rm(subpath?: string): Promise<void>;
|
|
24
43
|
/**
|
|
25
|
-
|
|
26
|
-
|
|
44
|
+
* Copy a file or directory into the fixture directory.
|
|
45
|
+
*
|
|
46
|
+
* @param sourcePath - The source path to copy from
|
|
47
|
+
* @param destinationSubpath - Optional destination path within the fixture.
|
|
48
|
+
* If omitted, uses the basename of sourcePath.
|
|
49
|
+
* If ends with path separator, appends basename of sourcePath.
|
|
50
|
+
* @param options - Copy options (e.g., recursive, filter)
|
|
51
|
+
* @returns Promise that resolves when copy is complete
|
|
52
|
+
*/
|
|
27
53
|
cp(sourcePath: string, destinationSubpath?: string, options?: CopyOptions): Promise<void>;
|
|
28
54
|
/**
|
|
29
|
-
|
|
30
|
-
|
|
55
|
+
* Create a new folder in the fixture directory (including parent directories).
|
|
56
|
+
*
|
|
57
|
+
* @param folderPath - The folder path to create within the fixture
|
|
58
|
+
* @returns Promise that resolves when directory is created
|
|
59
|
+
*/
|
|
31
60
|
mkdir(folderPath: string): Promise<string | undefined>;
|
|
32
61
|
/**
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
62
|
+
* Read a file from the fixture directory.
|
|
63
|
+
*
|
|
64
|
+
* @param filePath - The file path within the fixture to read
|
|
65
|
+
* @param options - Optional encoding or read options.
|
|
66
|
+
* When encoding is specified, returns a string; otherwise returns a Buffer.
|
|
67
|
+
* @returns Promise resolving to file contents as string or Buffer
|
|
68
|
+
*/
|
|
69
|
+
readFile: typeof fs.readFile;
|
|
36
70
|
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
71
|
+
* Read the contents of a directory in the fixture.
|
|
72
|
+
*
|
|
73
|
+
* @param directoryPath - The directory path within the fixture to read.
|
|
74
|
+
* Defaults to the fixture root when empty string is passed.
|
|
75
|
+
* @param options - Optional read directory options.
|
|
76
|
+
* Use `{ withFileTypes: true }` to get Dirent objects.
|
|
77
|
+
* @returns Promise resolving to array of file/directory names or Dirent objects
|
|
78
|
+
*/
|
|
79
|
+
readdir: typeof fs.readdir;
|
|
40
80
|
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
81
|
+
* Create or overwrite a file in the fixture directory.
|
|
82
|
+
*
|
|
83
|
+
* @param filePath - The file path within the fixture to write
|
|
84
|
+
* @param data - The content to write (string or Buffer)
|
|
85
|
+
* @param options - Optional encoding or write options
|
|
86
|
+
* @returns Promise that resolves when file is written
|
|
87
|
+
*/
|
|
88
|
+
writeFile: typeof fs.writeFile;
|
|
89
|
+
/**
|
|
90
|
+
* Read and parse a JSON file from the fixture directory.
|
|
91
|
+
*
|
|
92
|
+
* @param filePath - The JSON file path within the fixture to read
|
|
93
|
+
* @returns Promise resolving to the parsed JSON content
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* const data = await fixture.readJson<{ name: string }>('config.json')
|
|
98
|
+
* console.log(data.name) // Typed as string
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
readJson<T = unknown>(filePath: string): Promise<T>;
|
|
102
|
+
/**
|
|
103
|
+
* Create or overwrite a JSON file in the fixture directory.
|
|
104
|
+
*
|
|
105
|
+
* @param filePath - The JSON file path within the fixture to write
|
|
106
|
+
* @param json - The data to serialize as JSON
|
|
107
|
+
* @param space - Number of spaces or string to use for indentation. Defaults to 2.
|
|
108
|
+
* @returns Promise that resolves when file is written
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* // Default 2-space indentation
|
|
113
|
+
* await fixture.writeJson('config.json', { key: 'value' })
|
|
114
|
+
*
|
|
115
|
+
* // 4-space indentation
|
|
116
|
+
* await fixture.writeJson('config.json', { key: 'value' }, 4)
|
|
117
|
+
*
|
|
118
|
+
* // Tab indentation
|
|
119
|
+
* await fixture.writeJson('config.json', { key: 'value' }, '\t')
|
|
120
|
+
*
|
|
121
|
+
* // Minified (no formatting)
|
|
122
|
+
* await fixture.writeJson('config.json', { key: 'value' }, 0)
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
writeJson(filePath: string, json: unknown, space?: string | number): Promise<void>;
|
|
45
126
|
/**
|
|
46
127
|
* Resource management cleanup
|
|
47
128
|
* https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html
|
|
@@ -50,27 +131,32 @@ declare class FsFixture {
|
|
|
50
131
|
}
|
|
51
132
|
type FsFixtureType = FsFixture;
|
|
52
133
|
|
|
134
|
+
declare class PathBase {
|
|
135
|
+
readonly path: string;
|
|
136
|
+
constructor(path: string);
|
|
137
|
+
}
|
|
138
|
+
|
|
53
139
|
type SymlinkType = 'file' | 'dir' | 'junction';
|
|
54
|
-
declare class Symlink {
|
|
55
|
-
target: string;
|
|
56
|
-
type?: SymlinkType;
|
|
57
|
-
|
|
58
|
-
constructor(target: string, type?: SymlinkType);
|
|
140
|
+
declare class Symlink extends PathBase {
|
|
141
|
+
readonly target: string;
|
|
142
|
+
readonly type?: SymlinkType | undefined;
|
|
143
|
+
constructor(target: string, type?: SymlinkType | undefined, filePath?: string);
|
|
59
144
|
}
|
|
145
|
+
|
|
60
146
|
type ApiBase = {
|
|
61
147
|
fixturePath: string;
|
|
62
148
|
getPath(...subpaths: string[]): string;
|
|
63
149
|
symlink(targetPath: string,
|
|
64
150
|
/**
|
|
65
|
-
|
|
66
|
-
|
|
151
|
+
* Symlink type for Windows. Defaults to auto-detect by Node.
|
|
152
|
+
*/
|
|
67
153
|
type?: SymlinkType): Symlink;
|
|
68
154
|
};
|
|
69
155
|
type Api = ApiBase & {
|
|
70
156
|
filePath: string;
|
|
71
157
|
};
|
|
72
158
|
type FileTree = {
|
|
73
|
-
[path: string]: string | FileTree | ((api: Api) => string | Symlink);
|
|
159
|
+
[path: string]: string | Buffer | FileTree | ((api: Api) => string | Buffer | Symlink);
|
|
74
160
|
};
|
|
75
161
|
|
|
76
162
|
type FilterFunction = CopyOptions['filter'];
|
|
@@ -86,6 +172,36 @@ type CreateFixtureOptions = {
|
|
|
86
172
|
*/
|
|
87
173
|
templateFilter?: FilterFunction;
|
|
88
174
|
};
|
|
175
|
+
/**
|
|
176
|
+
* Create a temporary test fixture directory.
|
|
177
|
+
*
|
|
178
|
+
* @param source - Optional source to create the fixture from:
|
|
179
|
+
* - If omitted, creates an empty fixture directory
|
|
180
|
+
* - If a string, copies the directory at that path to the fixture
|
|
181
|
+
* - If a FileTree object, creates files and directories from the object structure
|
|
182
|
+
* @param options - Optional configuration for fixture creation
|
|
183
|
+
* @returns Promise resolving to an FsFixture instance
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```ts
|
|
187
|
+
* // Create empty fixture
|
|
188
|
+
* const fixture = await createFixture()
|
|
189
|
+
*
|
|
190
|
+
* // Create from object
|
|
191
|
+
* const fixture = await createFixture({
|
|
192
|
+
* 'file.txt': 'content',
|
|
193
|
+
* 'dir/nested.txt': 'nested content',
|
|
194
|
+
* 'binary.bin': Buffer.from('binary'),
|
|
195
|
+
* })
|
|
196
|
+
*
|
|
197
|
+
* // Create from template directory
|
|
198
|
+
* const fixture = await createFixture('./my-template')
|
|
199
|
+
*
|
|
200
|
+
* // Cleanup
|
|
201
|
+
* await fixture.rm()
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
89
204
|
declare const createFixture: (source?: string | FileTree, options?: CreateFixtureOptions) => Promise<FsFixture>;
|
|
90
205
|
|
|
91
|
-
export {
|
|
206
|
+
export { createFixture };
|
|
207
|
+
export type { CreateFixtureOptions, FileTree, FsFixtureType as FsFixture };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var F=Object.defineProperty;var a=(i,t)=>F(i,"name",{value:t,configurable:!0});import n from"node:fs/promises";import c from"node:path";import P from"node:fs";import b from"node:os";typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class k{static{a(this,"FsFixture")}path;constructor(t){this.path=t}getPath(...t){return c.join(this.path,...t)}exists(t=""){return n.access(this.getPath(t)).then(()=>!0,()=>!1)}rm(t=""){return n.rm(this.getPath(t),{recursive:!0,force:!0})}cp(t,e,s){return e?e.endsWith(c.sep)&&(e+=c.basename(t)):e=c.basename(t),n.cp(t,this.getPath(e),s)}mkdir(t){return n.mkdir(this.getPath(t),{recursive:!0})}readFile=a(((t,e)=>n.readFile(this.getPath(t),e)),"readFile");readdir=a(((t,e)=>n.readdir(this.getPath(t||""),e)),"readdir");writeFile=a(((t,e,...s)=>n.writeFile(this.getPath(t),e,...s)),"writeFile");async readJson(t){const e=await this.readFile(t,"utf8");return JSON.parse(e)}writeJson(t,e,s=2){return this.writeFile(t,JSON.stringify(e,null,s))}async[Symbol.asyncDispose](){await this.rm()}}const v=P.realpathSync(b.tmpdir()),D=`fs-fixture-${Date.now()}-${process.pid}`;let h=0;const j=a(()=>(h+=1,h),"getId");class f{static{a(this,"PathBase")}constructor(t){this.path=t}}class m extends f{static{a(this,"Directory")}}class y extends f{static{a(this,"File")}constructor(t,e){super(t),this.content=e}}class u extends f{static{a(this,"Symlink")}constructor(t,e,s){super(s??""),this.target=t,this.type=e}}const d=a((i,t,e)=>{const s=[];for(const p in i){if(!Object.hasOwn(i,p))continue;const r=c.join(t,p);let o=i[p];if(typeof o=="function"){const w=Object.assign(Object.create(e),{filePath:r}),l=o(w);if(l instanceof u){const g=new u(l.target,l.type,r);s.push(g);continue}else o=l}typeof o=="string"||Buffer.isBuffer(o)?s.push(new y(r,o)):s.push(new m(r),...d(o,r,e))}return s},"flattenFileTree"),x=a(async(i,t)=>{const e=t?.tempDir?c.resolve(t.tempDir):v,s=c.join(e,`${D}-${j()}/`);if(await n.mkdir(s,{recursive:!0}),i){if(typeof i=="string")await n.cp(i,s,{recursive:!0,filter:t?.templateFilter});else if(typeof i=="object"){const p={fixturePath:s,getPath:a((...r)=>c.join(s,...r),"getPath"),symlink:a((r,o)=>new u(r,o),"symlink")};await Promise.all(d(i,s,p).map(async r=>{r instanceof m?await n.mkdir(r.path,{recursive:!0}):r instanceof u?(await n.mkdir(c.dirname(r.path),{recursive:!0}),await n.symlink(r.target,r.path,r.type)):r instanceof y&&(await n.mkdir(c.dirname(r.path),{recursive:!0}),await n.writeFile(r.path,r.content))}))}}return new k(s)},"createFixture");export{x as createFixture};
|