Skip to content

Commit b8357cf

Browse files
committed
refactor!: organise exports in a more friendly way
Added helpers package for helpers that can be used by developers, and instead moved internal functions into utils
1 parent 35a0fc4 commit b8357cf

File tree

7 files changed

+406
-388
lines changed

7 files changed

+406
-388
lines changed

src/constants.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,3 @@ export const FIXTURE_METADATA_SYMBOL = Symbol("testdir-metadata");
2626
* The base directory for test directories.
2727
*/
2828
export const BASE_DIR = ".vitest-testdirs";
29-
30-
/**
31-
* Regular expression that matches any character that is not a word character or hyphen.
32-
* Used for validating and sanitizing directory names.
33-
* - Matches any character that is not [A-Za-z0-9_-]
34-
* - Global flag (g) enables matching all occurrences
35-
*/
36-
export const DIR_REGEX = /[^\w\-]+/g;

src/index.ts

Lines changed: 11 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* ```
1717
*/
1818

19-
import type { DirectoryJSON } from "./types";
19+
import type { DirectoryJSON, FromFileSystemOptions } from "./types";
2020
import { linkSync, mkdirSync, readdirSync, readFileSync, readlinkSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
2121
import {
2222
link,
@@ -43,25 +43,12 @@ import {
4343
getCurrentSuite,
4444
getCurrentTest,
4545
} from "vitest/suite";
46-
import { FIXTURE_ORIGINAL_PATH } from "./";
47-
48-
import {
49-
BASE_DIR,
50-
FIXTURE_METADATA,
51-
} from "./constants";
52-
import {
53-
createDirnameFromTask,
54-
hasMetadata,
55-
isDirectory,
56-
isDirectorySync,
57-
isLink,
58-
isPrimitive,
59-
isSymlink,
60-
symlink as symlinkFn,
61-
} from "./utils";
46+
import { BASE_DIR, FIXTURE_METADATA_SYMBOL, FIXTURE_ORIGINAL_PATH_SYMBOL } from "./constants";
47+
import { hasMetadata, isLink, isPrimitive, isSymlink } from "./helpers";
48+
import { createDirnameFromTask, isDirectory, isDirectorySync, isInVitest, processDirectory, processDirectorySync } from "./utils";
6249

6350
export * from "./constants";
64-
export * from "./utils";
51+
export * from "./helpers";
6552

6653
/**
6754
* Options for creating a test directory.
@@ -219,7 +206,7 @@ export async function createFileTree(
219206
): Promise<void> {
220207
for (let filename in files) {
221208
let data = files[filename];
222-
const metadata = hasMetadata(data) ? data[FIXTURE_METADATA] : undefined;
209+
const metadata = hasMetadata(data) ? data[FIXTURE_METADATA_SYMBOL] : undefined;
223210
data = hasMetadata(data) ? data.content : data;
224211

225212
filename = resolve(path, filename);
@@ -231,8 +218,8 @@ export async function createFileTree(
231218
}
232219

233220
if (isSymlink(data)) {
234-
if (files[FIXTURE_ORIGINAL_PATH] != null) {
235-
const original = normalize(files[FIXTURE_ORIGINAL_PATH]);
221+
if (files[FIXTURE_ORIGINAL_PATH_SYMBOL] != null) {
222+
const original = normalize(files[FIXTURE_ORIGINAL_PATH_SYMBOL]);
236223

237224
// we need to replace here due to the fact that we call `createFileTree` recursively,
238225
// and when we do it with a nested directory, the path is now the full path, and not just the relative path.
@@ -303,7 +290,7 @@ export async function createFileTree(
303290
export function createFileTreeSync(path: string, files: DirectoryJSON): void {
304291
for (let filename in files) {
305292
let data = files[filename];
306-
const metadata = hasMetadata(data) ? data[FIXTURE_METADATA] : undefined;
293+
const metadata = hasMetadata(data) ? data[FIXTURE_METADATA_SYMBOL] : undefined;
307294
data = hasMetadata(data) ? data.content : data;
308295

309296
filename = resolve(path, filename);
@@ -315,8 +302,8 @@ export function createFileTreeSync(path: string, files: DirectoryJSON): void {
315302
}
316303

317304
if (isSymlink(data)) {
318-
if (files[FIXTURE_ORIGINAL_PATH] != null) {
319-
const original = normalize(files[FIXTURE_ORIGINAL_PATH]);
305+
if (files[FIXTURE_ORIGINAL_PATH_SYMBOL] != null) {
306+
const original = normalize(files[FIXTURE_ORIGINAL_PATH_SYMBOL]);
320307

321308
// we need to replace here due to the fact that we call `createFileTree` recursively,
322309
// and when we do it with a nested directory, the path is now the full path, and not just the relative path.
@@ -377,161 +364,8 @@ export function createFileTreeSync(path: string, files: DirectoryJSON): void {
377364
}
378365
}
379366

380-
function isInVitest(): boolean {
381-
return getCurrentSuite() != null || getCurrentTest() != null;
382-
}
383-
384-
/**
385-
* Options for customizing the behavior of the fromFileSystem functions.
386-
*/
387-
export interface FromFileSystemOptions {
388-
/**
389-
* An array of file names to
390-
* ignore when reading the directory.
391-
*
392-
* @default []
393-
*
394-
* @example
395-
* ```ts
396-
* const files = await fromFileSystem("path/to/dir", {
397-
* ignore: ["node_modules", ".git"],
398-
* });
399-
* ```
400-
*/
401-
ignore?: string[];
402-
403-
/**
404-
* Whether to follow symbolic links.
405-
* @default true
406-
*/
407-
followLinks?: boolean;
408-
409-
/**
410-
* An object with extra files to include in the directory structure.
411-
* @default {}
412-
*
413-
* @example
414-
* ```ts
415-
* const files = await fromFileSystem("path/to/dir", {
416-
* extras: {
417-
* "extra-file.txt": "This is an extra file",
418-
* },
419-
* });
420-
* ```
421-
*/
422-
extras?: DirectoryJSON;
423-
424-
/**
425-
* A function that determines the encoding to be used for a file.
426-
* @default utf-8
427-
*
428-
* @example
429-
* ```ts
430-
* const files = await fromFileSystem("path/to/dir", {
431-
* getEncodingForFile: (path) => "utf-8",
432-
* });
433-
* ```
434-
*/
435-
getEncodingForFile?: EncodingForFileFn;
436-
}
437-
438367
const DEFAULT_ENCODING_FOR_FILE_FN = () => "utf-8" as BufferEncoding;
439368

440-
/**
441-
* A function type that determines the encoding for a given file path.
442-
* @param {string} path - The path to the file.
443-
* @returns {BufferEncoding | null} The encoding to be used for the file, as a {@link BufferEncoding}.
444-
*/
445-
export type EncodingForFileFn = (path: string) => BufferEncoding | null;
446-
447-
/**
448-
* Processes a directory and its contents recursively, creating a JSON representation of the file system.
449-
*
450-
* @param {string} path - The absolute path to the directory to process
451-
* @param {Required<Omit<FromFileSystemOptions, "extras">>} options - Configuration options for processing the directory
452-
*
453-
* @returns {Promise<DirectoryJSON>} A Promise that resolves to a DirectoryJSON object representing the directory structure
454-
* where keys are file/directory names and values are either:
455-
* - A string containing file contents for regular files
456-
* - A DirectoryJSON object for subdirectories
457-
* - A symbolic link representation for symlinks (when followLinks is true)
458-
*
459-
* @throws {Error} If there are issues reading the directory or its contents
460-
*/
461-
async function processDirectory(
462-
path: string,
463-
options: Required<Omit<FromFileSystemOptions, "extras">>,
464-
): Promise<DirectoryJSON> {
465-
const files: DirectoryJSON = {
466-
[FIXTURE_ORIGINAL_PATH]: normalize(path),
467-
};
468-
469-
const dirFiles = await readdir(path, {
470-
withFileTypes: true,
471-
});
472-
473-
const filteredFiles = dirFiles.filter((file) => !options.ignore.includes(file.name));
474-
475-
for (const file of filteredFiles) {
476-
const filePath = file.name;
477-
const fullPath = `${path}/${filePath}`;
478-
479-
if (file.isDirectory()) {
480-
files[filePath] = await processDirectory(fullPath, options);
481-
} else if (options.followLinks && file.isSymbolicLink()) {
482-
files[filePath] = symlinkFn(await readlink(fullPath));
483-
} else {
484-
files[filePath] = await readFile(fullPath, options.getEncodingForFile(fullPath));
485-
}
486-
}
487-
488-
return files;
489-
}
490-
491-
/**
492-
* Recursively processes a directory and returns its structure as a JSON object.
493-
*
494-
* @param {string} path - The absolute path to the directory to process
495-
* @param {Required<Omit<FromFileSystemOptions, "extras">>} options - Configuration options for processing the directory
496-
*
497-
* @returns {DirectoryJSON} A DirectoryJSON object representing the directory structure where:
498-
* - Keys are file/directory names
499-
* - Values are either:
500-
* - String content for files
501-
* - Nested DirectoryJSON objects for directories
502-
* - Symlink objects for symbolic links (when followLinks is true)
503-
* - Special key [FIXTURE_ORIGINAL_PATH] contains the normalized original path
504-
*/
505-
export function processDirectorySync(
506-
path: string,
507-
options: Required<Omit<FromFileSystemOptions, "extras">>,
508-
): DirectoryJSON {
509-
const files: DirectoryJSON = {
510-
[FIXTURE_ORIGINAL_PATH]: normalize(path),
511-
};
512-
513-
const dirFiles = readdirSync(path, {
514-
withFileTypes: true,
515-
});
516-
517-
const filteredFiles = dirFiles.filter((file) => !options.ignore.includes(file.name));
518-
519-
for (const file of filteredFiles) {
520-
const filePath = file.name;
521-
const fullPath = `${path}/${filePath}`;
522-
523-
if (file.isDirectory()) {
524-
files[filePath] = processDirectorySync(fullPath, options);
525-
} else if (options.followLinks && file.isSymbolicLink()) {
526-
files[filePath] = symlinkFn(readlinkSync(fullPath));
527-
} else {
528-
files[filePath] = readFileSync(fullPath, options.getEncodingForFile(fullPath));
529-
}
530-
}
531-
532-
return files;
533-
}
534-
535369
/**
536370
* Recursively reads a directory and returns a JSON representation of its structure
537371
*

src/types.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,64 @@ export interface TestdirMetadata {
9898
*/
9999
content: DirectoryContent | DirectoryJSON;
100100
}
101+
102+
/**
103+
* Options for customizing the behavior of the fromFileSystem functions.
104+
*/
105+
export interface FromFileSystemOptions {
106+
/**
107+
* An array of file names to
108+
* ignore when reading the directory.
109+
*
110+
* @default []
111+
*
112+
* @example
113+
* ```ts
114+
* const files = await fromFileSystem("path/to/dir", {
115+
* ignore: ["node_modules", ".git"],
116+
* });
117+
* ```
118+
*/
119+
ignore?: string[];
120+
121+
/**
122+
* Whether to follow symbolic links.
123+
* @default true
124+
*/
125+
followLinks?: boolean;
126+
127+
/**
128+
* An object with extra files to include in the directory structure.
129+
* @default {}
130+
*
131+
* @example
132+
* ```ts
133+
* const files = await fromFileSystem("path/to/dir", {
134+
* extras: {
135+
* "extra-file.txt": "This is an extra file",
136+
* },
137+
* });
138+
* ```
139+
*/
140+
extras?: DirectoryJSON;
141+
142+
/**
143+
* A function that determines the encoding to be used for a file.
144+
* @default utf-8
145+
*
146+
* @example
147+
* ```ts
148+
* const files = await fromFileSystem("path/to/dir", {
149+
* getEncodingForFile: (path) => "utf-8",
150+
* });
151+
* ```
152+
*/
153+
getEncodingForFile?: EncodingForFileFn;
154+
}
155+
156+
/**
157+
* A function type that determines the encoding for a given file path.
158+
* @param {string} path - The path to the file.
159+
* @returns {BufferEncoding | null} The encoding to be used for the file, as a {@link BufferEncoding}.
160+
*/
161+
export type EncodingForFileFn = (path: string) => BufferEncoding | null;

0 commit comments

Comments
 (0)