fs-fixture 2.11.0 → 2.12.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 +13 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -2
- package/skills/fs-fixture/SKILL.md +95 -0
package/README.md
CHANGED
|
@@ -149,6 +149,19 @@ const fixture = await createFixture({
|
|
|
149
149
|
})
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
+
**Symlinks:**
|
|
153
|
+
```ts
|
|
154
|
+
const fixture = await createFixture({
|
|
155
|
+
'index.js': 'import pkg from \'pkg\'',
|
|
156
|
+
|
|
157
|
+
// Symlink individual file or directory
|
|
158
|
+
'node_modules/pkg': ({ symlink }) => symlink(process.cwd()),
|
|
159
|
+
|
|
160
|
+
// Symlink entire directory (useful for sharing node_modules)
|
|
161
|
+
node_modules: ({ symlink }) => symlink(path.resolve('node_modules'))
|
|
162
|
+
})
|
|
163
|
+
```
|
|
164
|
+
|
|
152
165
|
**Binary files:**
|
|
153
166
|
```ts
|
|
154
167
|
const fixture = await createFixture({
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var g=Object.defineProperty;var c=(s,e)=>g(s,"name",{value:e,configurable:!0});var n=require("node:fs/promises"),f=require("node:path"),P=require("node:url"),v=require("node:fs"),F=require("node:os");typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class b{static{c(this,"FsFixture")}path;constructor(e){this.path=e}getPath(...e){return f.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,i){return t?t.endsWith(f.sep)&&(t+=f.basename(e)):t=f.basename(e),n.cp(e,this.getPath(t),i)}mkdir(e){return n.mkdir(this.getPath(e),{recursive:!0})}mv(e,t){return n.rename(this.getPath(e),this.getPath(t))}readFile=c(((e,t)=>n.readFile(this.getPath(e),t)),"readFile");readdir=c(((e,t)=>n.readdir(this.getPath(e||""),t)),"readdir");writeFile=c(((e,t,...i)=>n.writeFile(this.getPath(e),t,...i)),"writeFile");async readJson(e){const t=await this.readFile(e,"utf8");return JSON.parse(t)}writeJson(e,t,i=2){return this.writeFile(e,JSON.stringify(t,null,i))}async[Symbol.asyncDispose](){await this.rm()}}const D=v.realpathSync(F.tmpdir());class p{static{c(this,"PathBase")}constructor(e){this.path=e}}class y extends p{static{c(this,"Directory")}}class m extends p{static{c(this,"File")}constructor(e,t){super(e),this.content=t}}class u extends p{static{c(this,"Symlink")}constructor(e,t,i){super(i??""),this.target=e,this.type=t}}const d=c((s,e,t)=>{const i=[];for(const h in s){if(!Object.hasOwn(s,h))continue;const o=f.join(e,h);let a=s[h];if(typeof a=="function"){const r=Object.assign(Object.create(t),{filePath:o}),l=a(r);if(l instanceof u){const w=new u(l.target,l.type,o);i.push(w);continue}else a=l}if(typeof a=="string"||Buffer.isBuffer(a))i.push(new m(o,a));else if(a&&typeof a=="object"&&!Array.isArray(a))i.push(new y(o),...d(a,o,t));else throw new TypeError(`Invalid file content for path "${o}". Functions must return a string, Buffer, Symlink, or a nested FileTree object. Received: ${String(a)}`)}return i},"flattenFileTree"),j=c(async(s,e)=>{const t=e?.tempDir?f.resolve(typeof e.tempDir=="string"?e.tempDir:P.fileURLToPath(e.tempDir)):D;e?.tempDir&&await n.mkdir(t,{recursive:!0});const i=await n.mkdtemp(f.join(t,"fs-fixture-"));if(s){if(typeof s=="string")await n.cp(s,i,{recursive:!0,filter:e?.templateFilter});else if(typeof s=="object"){const o=d(s,i,{fixturePath:i,getPath:c((...r)=>f.join(i,...r),"getPath"),symlink:c((r,l)=>new u(r,l),"symlink")}),a=new Set;for(const r of o)r instanceof y?a.add(r.path):(r instanceof m||r instanceof u)&&a.add(f.dirname(r.path));await Promise.all(Array.from(a).map(r=>n.mkdir(r,{recursive:!0}))),await Promise.all(o.map(async r=>{r instanceof u?await n.symlink(r.target,r.path,r.type):r instanceof m&&await n.writeFile(r.path,r.content)}))}}return new b(i)},"createFixture");exports.createFixture=j;
|
|
1
|
+
"use strict";var g=Object.defineProperty;var c=(s,e)=>g(s,"name",{value:e,configurable:!0});var n=require("node:fs/promises"),f=require("node:path"),P=require("node:url"),v=require("node:fs"),F=require("node:os");typeof Symbol.asyncDispose!="symbol"&&Object.defineProperty(Symbol,"asyncDispose",{configurable:!1,enumerable:!1,writable:!1,value:Symbol.for("asyncDispose")});class b{static{c(this,"FsFixture")}path;constructor(e){this.path=e}getPath(...e){return f.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,i){return t?(t.endsWith("/")||t.endsWith(f.sep))&&(t+=f.basename(e)):t=f.basename(e),n.cp(e,this.getPath(t),i)}mkdir(e){return n.mkdir(this.getPath(e),{recursive:!0})}mv(e,t){return n.rename(this.getPath(e),this.getPath(t))}readFile=c(((e,t)=>n.readFile(this.getPath(e),t)),"readFile");readdir=c(((e,t)=>n.readdir(this.getPath(e||""),t)),"readdir");writeFile=c(((e,t,...i)=>n.writeFile(this.getPath(e),t,...i)),"writeFile");async readJson(e){const t=await this.readFile(e,"utf8");return JSON.parse(t)}writeJson(e,t,i=2){return this.writeFile(e,JSON.stringify(t,null,i))}async[Symbol.asyncDispose](){await this.rm()}}const D=v.realpathSync(F.tmpdir());class p{static{c(this,"PathBase")}constructor(e){this.path=e}}class y extends p{static{c(this,"Directory")}}class m extends p{static{c(this,"File")}constructor(e,t){super(e),this.content=t}}class u extends p{static{c(this,"Symlink")}constructor(e,t,i){super(i??""),this.target=e,this.type=t}}const d=c((s,e,t)=>{const i=[];for(const h in s){if(!Object.hasOwn(s,h))continue;const o=f.join(e,h);let a=s[h];if(typeof a=="function"){const r=Object.assign(Object.create(t),{filePath:o}),l=a(r);if(l instanceof u){const w=new u(l.target,l.type,o);i.push(w);continue}else a=l}if(typeof a=="string"||Buffer.isBuffer(a))i.push(new m(o,a));else if(a&&typeof a=="object"&&!Array.isArray(a))i.push(new y(o),...d(a,o,t));else throw new TypeError(`Invalid file content for path "${o}". Functions must return a string, Buffer, Symlink, or a nested FileTree object. Received: ${String(a)}`)}return i},"flattenFileTree"),j=c(async(s,e)=>{const t=e?.tempDir?f.resolve(typeof e.tempDir=="string"?e.tempDir:P.fileURLToPath(e.tempDir)):D;e?.tempDir&&await n.mkdir(t,{recursive:!0});const i=await n.mkdtemp(f.join(t,"fs-fixture-"));if(s){if(typeof s=="string")await n.cp(s,i,{recursive:!0,filter:e?.templateFilter});else if(typeof s=="object"){const o=d(s,i,{fixturePath:i,getPath:c((...r)=>f.join(i,...r),"getPath"),symlink:c((r,l)=>new u(r,l),"symlink")}),a=new Set;for(const r of o)r instanceof y?a.add(r.path):(r instanceof m||r instanceof u)&&a.add(f.dirname(r.path));await Promise.all(Array.from(a).map(r=>n.mkdir(r,{recursive:!0}))),await Promise.all(o.map(async r=>{r instanceof u?await n.symlink(r.target,r.path,r.type):r instanceof m&&await n.writeFile(r.path,r.content)}))}}return new b(i)},"createFixture");exports.createFixture=j;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var g=Object.defineProperty;var o=(s,e)=>g(s,"name",{value:e,configurable:!0});import n from"node:fs/promises";import f from"node:path";import{fileURLToPath as P}from"node:url";import F 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 D{static{o(this,"FsFixture")}path;constructor(e){this.path=e}getPath(...e){return f.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,i){return t?t.endsWith(f.sep)&&(t+=f.basename(e)):t=f.basename(e),n.cp(e,this.getPath(t),i)}mkdir(e){return n.mkdir(this.getPath(e),{recursive:!0})}mv(e,t){return n.rename(this.getPath(e),this.getPath(t))}readFile=o(((e,t)=>n.readFile(this.getPath(e),t)),"readFile");readdir=o(((e,t)=>n.readdir(this.getPath(e||""),t)),"readdir");writeFile=o(((e,t,...i)=>n.writeFile(this.getPath(e),t,...i)),"writeFile");async readJson(e){const t=await this.readFile(e,"utf8");return JSON.parse(t)}writeJson(e,t,i=2){return this.writeFile(e,JSON.stringify(t,null,i))}async[Symbol.asyncDispose](){await this.rm()}}const j=F.realpathSync(b.tmpdir());class h{static{o(this,"PathBase")}constructor(e){this.path=e}}class y extends h{static{o(this,"Directory")}}class u extends h{static{o(this,"File")}constructor(e,t){super(e),this.content=t}}class m extends h{static{o(this,"Symlink")}constructor(e,t,i){super(i??""),this.target=e,this.type=t}}const d=o((s,e,t)=>{const i=[];for(const p in s){if(!Object.hasOwn(s,p))continue;const c=f.join(e,p);let a=s[p];if(typeof a=="function"){const r=Object.assign(Object.create(t),{filePath:c}),l=a(r);if(l instanceof m){const w=new m(l.target,l.type,c);i.push(w);continue}else a=l}if(typeof a=="string"||Buffer.isBuffer(a))i.push(new u(c,a));else if(a&&typeof a=="object"&&!Array.isArray(a))i.push(new y(c),...d(a,c,t));else throw new TypeError(`Invalid file content for path "${c}". Functions must return a string, Buffer, Symlink, or a nested FileTree object. Received: ${String(a)}`)}return i},"flattenFileTree"),v=o(async(s,e)=>{const t=e?.tempDir?f.resolve(typeof e.tempDir=="string"?e.tempDir:P(e.tempDir)):j;e?.tempDir&&await n.mkdir(t,{recursive:!0});const i=await n.mkdtemp(f.join(t,"fs-fixture-"));if(s){if(typeof s=="string")await n.cp(s,i,{recursive:!0,filter:e?.templateFilter});else if(typeof s=="object"){const c=d(s,i,{fixturePath:i,getPath:o((...r)=>f.join(i,...r),"getPath"),symlink:o((r,l)=>new m(r,l),"symlink")}),a=new Set;for(const r of c)r instanceof y?a.add(r.path):(r instanceof u||r instanceof m)&&a.add(f.dirname(r.path));await Promise.all(Array.from(a).map(r=>n.mkdir(r,{recursive:!0}))),await Promise.all(c.map(async r=>{r instanceof m?await n.symlink(r.target,r.path,r.type):r instanceof u&&await n.writeFile(r.path,r.content)}))}}return new D(i)},"createFixture");export{v as createFixture};
|
|
1
|
+
var g=Object.defineProperty;var o=(s,e)=>g(s,"name",{value:e,configurable:!0});import n from"node:fs/promises";import f from"node:path";import{fileURLToPath as P}from"node:url";import F 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 D{static{o(this,"FsFixture")}path;constructor(e){this.path=e}getPath(...e){return f.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,i){return t?(t.endsWith("/")||t.endsWith(f.sep))&&(t+=f.basename(e)):t=f.basename(e),n.cp(e,this.getPath(t),i)}mkdir(e){return n.mkdir(this.getPath(e),{recursive:!0})}mv(e,t){return n.rename(this.getPath(e),this.getPath(t))}readFile=o(((e,t)=>n.readFile(this.getPath(e),t)),"readFile");readdir=o(((e,t)=>n.readdir(this.getPath(e||""),t)),"readdir");writeFile=o(((e,t,...i)=>n.writeFile(this.getPath(e),t,...i)),"writeFile");async readJson(e){const t=await this.readFile(e,"utf8");return JSON.parse(t)}writeJson(e,t,i=2){return this.writeFile(e,JSON.stringify(t,null,i))}async[Symbol.asyncDispose](){await this.rm()}}const j=F.realpathSync(b.tmpdir());class h{static{o(this,"PathBase")}constructor(e){this.path=e}}class y extends h{static{o(this,"Directory")}}class u extends h{static{o(this,"File")}constructor(e,t){super(e),this.content=t}}class m extends h{static{o(this,"Symlink")}constructor(e,t,i){super(i??""),this.target=e,this.type=t}}const d=o((s,e,t)=>{const i=[];for(const p in s){if(!Object.hasOwn(s,p))continue;const c=f.join(e,p);let a=s[p];if(typeof a=="function"){const r=Object.assign(Object.create(t),{filePath:c}),l=a(r);if(l instanceof m){const w=new m(l.target,l.type,c);i.push(w);continue}else a=l}if(typeof a=="string"||Buffer.isBuffer(a))i.push(new u(c,a));else if(a&&typeof a=="object"&&!Array.isArray(a))i.push(new y(c),...d(a,c,t));else throw new TypeError(`Invalid file content for path "${c}". Functions must return a string, Buffer, Symlink, or a nested FileTree object. Received: ${String(a)}`)}return i},"flattenFileTree"),v=o(async(s,e)=>{const t=e?.tempDir?f.resolve(typeof e.tempDir=="string"?e.tempDir:P(e.tempDir)):j;e?.tempDir&&await n.mkdir(t,{recursive:!0});const i=await n.mkdtemp(f.join(t,"fs-fixture-"));if(s){if(typeof s=="string")await n.cp(s,i,{recursive:!0,filter:e?.templateFilter});else if(typeof s=="object"){const c=d(s,i,{fixturePath:i,getPath:o((...r)=>f.join(i,...r),"getPath"),symlink:o((r,l)=>new m(r,l),"symlink")}),a=new Set;for(const r of c)r instanceof y?a.add(r.path):(r instanceof u||r instanceof m)&&a.add(f.dirname(r.path));await Promise.all(Array.from(a).map(r=>n.mkdir(r,{recursive:!0}))),await Promise.all(c.map(async r=>{r instanceof m?await n.symlink(r.target,r.path,r.type):r instanceof u&&await n.writeFile(r.path,r.content)}))}}return new D(i)},"createFixture");export{v as createFixture};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fs-fixture",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "Easily create test fixtures at a temporary file-system path",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"test",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"email": "hiroki.osame@gmail.com"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"dist"
|
|
23
|
+
"dist",
|
|
24
|
+
"skills"
|
|
24
25
|
],
|
|
25
26
|
"main": "./dist/index.cjs",
|
|
26
27
|
"module": "./dist/index.mjs",
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fs-fixture
|
|
3
|
+
description: Create disposable file system test fixtures from objects, templates, or empty directories with automatic cleanup. Use when writing tests that need temporary files, directories, symlinks, or JSON fixtures on disk. Keywords - fs, tmp, temp, fixture, testing utility, cleanup, dispose.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Quick Start
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createFixture } from 'fs-fixture'
|
|
10
|
+
|
|
11
|
+
// From object — keys are paths, values are content
|
|
12
|
+
const fixture = await createFixture({
|
|
13
|
+
'package.json': JSON.stringify({ name: 'test' }),
|
|
14
|
+
'src/index.js': 'export default 42',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Use fixture.path, fixture.readFile(), etc.
|
|
18
|
+
await fixture.rm() // Cleanup when done
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Auto-cleanup with `using` (TypeScript 5.2+):
|
|
22
|
+
```ts
|
|
23
|
+
await using fixture = await createFixture({ 'file.txt': 'content' })
|
|
24
|
+
// Automatically cleaned up when scope exits
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## createFixture(source?, options?)
|
|
28
|
+
|
|
29
|
+
| Source | Behavior |
|
|
30
|
+
|--------|----------|
|
|
31
|
+
| `{ path: content }` | Create files from object. Nested objects create directories. |
|
|
32
|
+
| `'./path'` | Copy template directory into fixture |
|
|
33
|
+
| _(omitted)_ | Empty temporary directory |
|
|
34
|
+
|
|
35
|
+
| Option | Type | Default | Description |
|
|
36
|
+
|--------|------|---------|-------------|
|
|
37
|
+
| `tempDir` | `string \| URL` | `os.tmpdir()` | Custom parent directory |
|
|
38
|
+
| `templateFilter` | `(src, dest) => boolean` | — | Filter files when copying template |
|
|
39
|
+
|
|
40
|
+
## FsFixture Methods
|
|
41
|
+
|
|
42
|
+
| Method | Description |
|
|
43
|
+
|--------|-------------|
|
|
44
|
+
| `fixture.path` | Absolute path to fixture root |
|
|
45
|
+
| `getPath(...paths)` | Resolve path relative to fixture root |
|
|
46
|
+
| `exists(path?)` | Check existence (returns `boolean`) |
|
|
47
|
+
| `readFile(path, encoding?)` | Read file — `'utf8'` returns `string`, omit for `Buffer` |
|
|
48
|
+
| `writeFile(path, content)` | Write string or Buffer |
|
|
49
|
+
| `readJson<T>(path)` | Read and parse JSON with type parameter |
|
|
50
|
+
| `writeJson(path, data, space?)` | Write JSON (default: 2-space indent, `0` for minified) |
|
|
51
|
+
| `readdir(path, options?)` | List directory (`{ withFileTypes: true }` for Dirent[]) |
|
|
52
|
+
| `mkdir(path)` | Create directory recursively |
|
|
53
|
+
| `cp(source, dest?)` | Copy external file/directory into fixture |
|
|
54
|
+
| `mv(source, dest)` | Move or rename within fixture |
|
|
55
|
+
| `rm(path?)` | Delete path, or entire fixture if omitted |
|
|
56
|
+
|
|
57
|
+
## FileTree Values
|
|
58
|
+
|
|
59
|
+
| Type | Example |
|
|
60
|
+
|------|---------|
|
|
61
|
+
| `string` | `'file content'` |
|
|
62
|
+
| `Buffer` | `Buffer.from([0x89, 0x50])` |
|
|
63
|
+
| Nested object | `{ dir: { 'file.txt': 'content' } }` |
|
|
64
|
+
| Function | `({ fixturePath, filePath, getPath, symlink }) => symlink('./target')` |
|
|
65
|
+
|
|
66
|
+
Path syntax — these are equivalent:
|
|
67
|
+
```ts
|
|
68
|
+
// Nested objects
|
|
69
|
+
{ src: { utils: { 'helper.js': 'code' } } }
|
|
70
|
+
|
|
71
|
+
// Slash-separated keys
|
|
72
|
+
{ 'src/utils/helper.js': 'code' }
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Patterns
|
|
76
|
+
|
|
77
|
+
### Symlinks
|
|
78
|
+
```ts
|
|
79
|
+
const fixture = await createFixture({
|
|
80
|
+
'target.txt': 'real file',
|
|
81
|
+
'link.txt': ({ symlink }) => symlink('./target.txt'),
|
|
82
|
+
'node_modules/pkg': ({ symlink }) => symlink(process.cwd()),
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Dynamic content
|
|
87
|
+
```ts
|
|
88
|
+
const fixture = await createFixture({
|
|
89
|
+
'info.txt': ({ fixturePath }) => `Root: ${fixturePath}`,
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Related
|
|
94
|
+
|
|
95
|
+
Commonly used with `manten` test framework.
|