Skip to content

fs ​

File system utilities, providing file operations, directory scanning, safe writing, change detection, report path resolution, file mapping, and batch deletion.

Import ​

typescript
// Submodule import (recommended)
import { checkSourceExists, copySourceToTarget, writeFileContent, scanDirectory, writeJsonReport, writeFileSyncSafely, shouldUpdateFileContent, resolveReportPath, scanAndMapFiles, deleteFiles, DirectoryWatcher } from '@meng-xi/vite-plugin/common/fs'
import type { CopyOptions, CopyResult, ScannedFile, ScanDirectoryOptions, DirectoryWatcherOptions } from '@meng-xi/vite-plugin/common/fs'

// Barrel import
import { checkSourceExists, copySourceToTarget, writeFileContent, scanDirectory, writeJsonReport, writeFileSyncSafely, shouldUpdateFileContent, resolveReportPath, scanAndMapFiles, deleteFiles, DirectoryWatcher } from '@meng-xi/vite-plugin/common'
import type { CopyOptions, CopyResult, ScannedFile, ScanDirectoryOptions, DirectoryWatcherOptions } from '@meng-xi/vite-plugin/common'

Type Exports ​

CopyOptions ​

Copy operation options interface.

typescript
interface CopyOptions {
	recursive: boolean // Whether to recursively copy subdirectories
	overwrite: boolean // Whether to overwrite existing files
	incremental?: boolean // Whether to only copy modified files (default false)
	parallelLimit?: number // Concurrency limit (default 10)
	skipEmptyDirs?: boolean // Whether to skip empty directories
}

CopyResult ​

Copy result interface.

typescript
interface CopyResult {
	copiedFiles: number // Number of copied files
	skippedFiles: number // Number of skipped files
	copiedDirs: number // Number of copied directories
	executionTime: number // Execution time in milliseconds
}

checkSourceExists ​

Check if source file exists, throw an error if not found.

typescript
async function checkSourceExists(sourcePath: string): Promise<void>

Parameters

ParameterTypeDescription
sourcePathstringSource path

Example

typescript
await checkSourceExists('/path/to/file')
// Throws if not found: Error: 复制文件失败:源文件不存在 - /path/to/file

copySourceToTarget ​

Copy files or directories to target location.

typescript
async function copySourceToTarget(sourcePath: string, targetPath: string, options: CopyOptions): Promise<CopyResult>

Parameters

ParameterTypeDescription
sourcePathstringSource path
targetPathstringTarget path
optionsCopyOptionsCopy options

CopyOptions

PropertyTypeDefaultDescription
recursiveboolean-Recursively copy subdirectories
overwriteboolean-Overwrite existing files
incrementalbooleanfalseOnly copy modified files
parallelLimitnumber10Concurrency limit

CopyResult

PropertyTypeDescription
copiedFilesnumberNumber of copied files
skippedFilesnumberNumber of skipped files
copiedDirsnumberNumber of copied dirs
executionTimenumberExecution time (ms)

Example

typescript
const result = await copySourceToTarget('src/assets', 'dist/assets', {
	recursive: true,
	overwrite: true,
	incremental: true,
	parallelLimit: 10
})

console.log(result)
// { copiedFiles: 10, skippedFiles: 2, copiedDirs: 3, executionTime: 150 }

writeFileContent ​

Write content to a file.

typescript
async function writeFileContent(filePath: string, content: string): Promise<void>

Parameters

ParameterTypeDescription
filePathstringFile path
contentstringFile content

Example

typescript
await writeFileContent('/path/to/file.txt', 'Hello World')

scanDirectory ​

Recursively scan a directory and collect file information.

typescript
async function scanDirectory(dirPath: string, options?: ScanDirectoryOptions): Promise<ScannedFile[]>

Parameters

ParameterTypeDefaultDescription
dirPathstring-Directory path
optionsScanDirectoryOptions{}Scan options

ScanDirectoryOptions

PropertyTypeDefaultDescription
includeExtensionsstring[][]File extensions to include, empty for all
excludePatternsstring[][]Path patterns to exclude
filter(filePath: string, extension: string, size: number) => boolean-Custom file filter function

ScannedFile

PropertyTypeDescription
filePathstringAbsolute file path
sizenumberFile size in bytes
extensionstringFile extension (lowercase, with dot)

Returns

Promise<ScannedFile[]> - List of file information

Example

typescript
// Scan all .js files
const jsFiles = await scanDirectory('dist', { includeExtensions: ['.js'] })

// Exclude node_modules
const files = await scanDirectory('dist', { excludePatterns: ['node_modules'] })

// Custom filter
const largeFiles = await scanDirectory('dist', {
	filter: (filePath, ext, size) => size > 1024
})

writeJsonReport ​

Write data to a JSON file.

typescript
async function writeJsonReport(filePath: string, data: object, indent?: number): Promise<void>

Parameters

ParameterTypeDefaultDescription
filePathstring-Output file path
dataobject-Data object to serialize
indentnumber2JSON indentation spaces

Example

typescript
await writeJsonReport('dist/report.json', { timestamp: Date.now(), stats: [] })
await writeJsonReport('dist/report.json', data, 4)

writeFileSyncSafely ​

Synchronously write file content, automatically creating non-existent directories.

typescript
function writeFileSyncSafely(filePath: string, content: string): void

Parameters

ParameterTypeDescription
filePathstringFile path
contentstringFile content

Notes

  • Synchronously writes file, automatically creates target directories recursively if they don't exist
  • Suitable for scenarios requiring synchronous writes in build hooks (e.g., transform hook)
  • Throws NodeJS.ErrnoException when file write fails (e.g., insufficient permissions)

Example

typescript
writeFileSyncSafely('/project/src/auto-imports.d.ts', 'declare global { ... }')

shouldUpdateFileContent ​

Check if file content needs to be updated (synchronous version).

typescript
function shouldUpdateFileContent(filePath: string, newContent: string): boolean

Parameters

ParameterTypeDescription
filePathstringFile path
newContentstringNewly generated file content

Returns

boolean - Returns true if update is needed, false otherwise

Notes

  • Compares existing file content with newly generated content, only needs to write when content has changed
  • Reduces unnecessary file IO operations
  • Returns true when file doesn't exist

Example

typescript
if (shouldUpdateFileContent('/project/src/auto-imports.d.ts', newContent)) {
	writeFileSyncSafely('/project/src/auto-imports.d.ts', newContent)
}

resolveReportPath ​

Resolve report output path.

typescript
function resolveReportPath(outDir: string, reportPath: string | false): string | null

Parameters

ParameterTypeDescription
outDirstringBuild output directory path
reportPathstring | falseReport file path, false to skip report generation

Returns

string | null - Resolved absolute path, returns null when reportPath is false

Notes

  • When reportPath is a relative path, it is resolved relative to outDir
  • When reportPath is an absolute path, it is used directly
  • When reportPath is false, returns null

Example

typescript
resolveReportPath('dist', 'report.json')   // 'dist/report.json'
resolveReportPath('dist', '/tmp/r.json')    // '/tmp/r.json'
resolveReportPath('dist', false)            // null

scanAndMapFiles ​

Scan a directory and map file information to custom structures.

typescript
async function scanAndMapFiles<T>(
  dirPath: string,
  params: {
    scanOptions?: ScanDirectoryOptions
    mapFn: (file: ScannedFile, dirPath: string) => T
  }
): Promise<T[]>

Parameters

ParameterTypeDefaultDescription
dirPathstring-Directory path
paramsobject-Scan and mapping parameters

params

PropertyTypeDefaultDescription
scanOptionsScanDirectoryOptions-Options passed to scanDirectory
mapFn(file: ScannedFile, dirPath: string) => T-Function to map ScannedFile to custom structure

Returns

Promise<T[]> - List of mapped entries

Notes

  • Recursively scans directory and applies mapFn to each file
  • Generic wrapper for the "scan + filter + map" pattern used by multiple plugins
  • mapFn receives the original ScannedFile and dirPath, allowing free computation of relativePath etc.

Example

typescript
// Scan and map to custom structure
const candidates = await scanAndMapFiles('dist', {
  scanOptions: { filter: (fp, ext, size) => size > 1024 },
  mapFn: (f, dir) => ({
    filePath: f.filePath,
    relativePath: normalizePath(path.relative(dir, f.filePath)),
    size: f.size,
    ext: f.extension
  })
})

deleteFiles ​

Batch delete a list of files.

typescript
async function deleteFiles(filePaths: string[]): Promise<void>

Parameters

ParameterTypeDescription
filePathsstring[]List of absolute file paths to delete (auto-deduplicated)

Notes

  • Deletes files by path list, auto-deduplicates then deletes one by one
  • Silently ignores deletion errors (e.g., file already deleted or insufficient permissions)

Example

typescript
await deleteFiles(['/dist/app.js', '/dist/app.js.gz'])

DirectoryWatcher ​

A recursive directory watcher that sets up recursive file watching on a set of directories, managing the start and stop of watchers uniformly.

typescript
class DirectoryWatcher {
  constructor(options: DirectoryWatcherOptions)
  start(): number
  stop(): void
  get size(): number
}

DirectoryWatcherOptions

PropertyTypeDescription
dirsstring[]Directories to watch (absolute paths; non-existent ones are skipped)
onChange(dir: string, eventType: string, filename: string | null) => voidCallback invoked when directory contents change
logger{ info(message: string): void; warn(message: string): void }Optional logger interface for status and warnings
labelstringOptional log label (e.g., 'page directory')

Methods

MethodReturnsDescription
startnumberStarts watching, returns number of dirs watched
stopvoidCloses all watchers and clears the list
sizenumberNumber of active watchers (read-only)

Notes

  • Uses fs.watch with the recursive option; supported on macOS/Linux. On unsupported platforms an error is thrown, which this class catches and skips that directory, preventing plugin crashes
  • Suitable for watching directories in dev mode to auto-regenerate outputs
  • Internal plugins (e.g., generatePages, generateUni) use it to watch page directories

Example

typescript
const watcher = new DirectoryWatcher({
  dirs: ['/abs/pages', '/abs/pages-sub'],
  onChange: () => regenerate(),
  logger: { info: console.log, warn: console.warn },
  label: 'page directory'
})

watcher.start()
// Watching page directory: /abs/pages, /abs/pages-sub
// ...
watcher.stop()

Released under the MIT License.