Cross-platform file loading utilities for locating, loading, and importing files throughout your project and node_modules. Provides cross-platform path resolution, Node modules discovery, dynamic import support, and project root (@/) path shortcuts for efficient file management across different environments.
import { NodeFS, FileLoader } from '@stackpress/lib';
const loader = new FileLoader(new NodeFS());Cross-platform file loading utilities that provide a unified interface for file operations across different environments. The FileLoader class abstracts filesystem operations and provides convenient methods for path resolution, module discovery, and dynamic imports.
The following properties are available when instantiating a FileLoader.
| Property | Type | Description |
|---|---|---|
cwd |
string |
Current working directory |
fs |
FileSystem |
Filesystem interface being used |
The following example shows how to get absolute paths from various path formats with automatic resolution.
// Project root paths (@ prefix)
const path1 = await loader.absolute('@/src/index.ts');
// Returns: /project/root/src/index.ts
// Relative paths
const path2 = await loader.absolute('./utils/helper.js');
// Returns: /current/directory/utils/helper.js
// Node modules
const path3 = await loader.absolute('@types/node');
// Returns: /project/node_modules/@types/node
// Absolute paths (unchanged)
const path4 = await loader.absolute('/usr/local/bin');
// Returns: /usr/local/binParameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Path to resolve (supports @/, ./, ../, and node_modules) |
pwd |
string |
Working directory (default: current working directory) |
Returns
A promise that resolves to the absolute path string.
The following example shows how to remove file extensions from paths for clean path manipulation.
const basePath = loader.basepath('/path/to/file.js');
// Returns: '/path/to/file'
const noExt = loader.basepath('/path/to/file');
// Returns: '/path/to/file' (unchanged if no extension)Parameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Path to process |
Returns
The path without the file extension.
The following example shows how to locate node_modules directories for package resolution.
// Find node_modules containing a specific package
const modulesPath = await loader.modules('@types/node');
// Returns: '/project/node_modules'
// Find node_modules from a specific directory
const modulesPath2 = await loader.modules('lodash', '/some/path');
// Returns: '/some/path/node_modules' or traverses upParameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Package name to locate |
pwd |
string |
Starting directory (default: current working directory) |
meta |
boolean |
Use import.meta.resolve if available (default: true) |
Returns
A promise that resolves to the node_modules directory path.
The following example shows how to locate the @stackpress/lib installation directory.
const libPath = await loader.lib();
// Returns: '/project/node_modules' (where @stackpress/lib is installed)
const libPath2 = await loader.lib('/custom/path');
// Returns: node_modules path containing @stackpress/lib from custom pathParameters
| Parameter | Type | Description |
|---|---|---|
pwd |
string |
Starting directory (default: current working directory) |
Returns
A promise that resolves to the node_modules directory containing @stackpress/lib.
The following example shows how to dynamically import various file types with type safety.
// Import JavaScript/TypeScript modules
const module = await loader.import('./utils/helper.js');
console.log(module.default); // Default export
console.log(module.namedExport); // Named export
// Import JSON files
const config = await loader.import('./config.json');
console.log(config); // Parsed JSON object
// Import with default extraction
const defaultExport = await loader.import('./module.js', true);
// Returns only the default exportParameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Path to the file to import |
getDefault |
boolean |
Return only the default export (default: false) |
Returns
A promise that resolves to the imported module or JSON data.
The following example shows how to get relative paths between files for import statements.
// Get relative path from source to target
const relativePath = loader.relative(
'/project/src/components/Button.js',
'/project/src/utils/helper.js'
);
// Returns: '../utils/helper'
// Include file extension
const relativeWithExt = loader.relative(
'/project/src/components/Button.js',
'/project/src/utils/helper.js',
true
);
// Returns: '../utils/helper.js'Parameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Source file path |
require |
string |
Target file path |
withExtname |
boolean |
Include file extension (default: false) |
Returns
The relative path from source to target.
The following example shows how to resolve and verify path existence with error handling.
// Resolve path (returns null if not found)
const resolved = await loader.resolve('./config.json');
// Returns: '/project/config.json' or null
// Resolve with existence check (throws if not found)
try {
const resolved = await loader.resolve('./config.json', process.cwd(), true);
console.log('File exists:', resolved);
} catch (error) {
console.log('File not found');
}
// Resolve from specific directory
const resolved2 = await loader.resolve('@/src/index.ts', '/custom/pwd');Parameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Path to resolve |
pwd |
string |
Working directory (default: current working directory) |
exists |
boolean |
Throw error if path doesn't exist (default: false) |
Returns
A promise that resolves to the absolute path or null if not found.
The following example shows how to resolve files with automatic extension detection and fallbacks.
// Try multiple extensions
const file = await loader.resolveFile('./module', ['.js', '.ts', '.json']);
// Tries: ./module.js, ./module.ts, ./module.json, ./module/index.js, etc.
// Resolve with existence check
try {
const file = await loader.resolveFile('./module', ['.js'], process.cwd(), true);
console.log('Found file:', file);
} catch (error) {
console.log('No file found with specified extensions');
}Parameters
| Parameter | Type | Description |
|---|---|---|
pathname |
string |
Path to resolve (without extension) |
extnames |
string[] |
File extensions to try (default: ['.js', '.json']) |
pwd |
string |
Working directory (default: current working directory) |
exists |
boolean |
Throw error if file doesn't exist (default: false) |
Returns
A promise that resolves to the resolved file path or null if not found.
FileLoader supports several path resolution patterns for flexible file access across different project structures and environments.
The @ symbol refers to the project root directory, providing a consistent way to reference files from the project base.
// @ refers to the project root (cwd)
await loader.absolute('@/src/index.ts');
await loader.absolute('@/config/database.json');Standard relative path resolution for files relative to the current working directory or specified path.
// Standard relative paths
await loader.absolute('./utils/helper.js');
await loader.absolute('../shared/constants.ts');Automatic resolution from node_modules directories with support for scoped packages and nested dependencies.
// Automatically resolves from node_modules
await loader.absolute('lodash');
await loader.absolute('@types/node');
await loader.absolute('@company/private-package');Absolute paths are returned as-is with symlink resolution for consistent file access.
// Absolute paths are returned as-is (with symlink resolution)
await loader.absolute('/usr/local/bin/node');
await loader.absolute('C:\\Program Files\\Node\\node.exe');FileLoader works with any FileSystem implementation, allowing for flexible filesystem backends and testing scenarios.
import { NodeFS } from '@stackpress/lib';
// Node.js filesystem
const nodeLoader = new FileLoader(new NodeFS());
// Custom filesystem (for testing, virtual fs, etc.)
class CustomFS implements FileSystem {
// Implement required methods...
}
const customLoader = new FileLoader(new CustomFS());FileLoader handles platform differences automatically to ensure consistent behavior across different operating systems and environments.
The following features ensure cross-platform compatibility.
- Path separators — Automatically uses correct separators (/ vs \)
- Symlinks — Resolves symbolic links to real paths
- Case sensitivity — Respects filesystem case sensitivity rules
- Module resolution — Works with both CommonJS and ES modules
FileLoader provides clear error messages for common issues to help with debugging and development.
try {
await loader.modules('non-existent-package');
} catch (error) {
// Error: Cannot find non-existent-package in any node_modules
}
try {
await loader.resolve('missing-file.js', process.cwd(), true);
} catch (error) {
// Error: Cannot resolve 'missing-file.js'
}The following examples demonstrate common usage patterns for the FileLoader in real-world scenarios.
// Load config with fallbacks
const config = await loader.import('@/config.json').catch(() => ({}));
// Load environment-specific config
const env = process.env.NODE_ENV || 'development';
const envConfig = await loader.import(`@/config/${env}.json`);// Resolve module paths for bundling
const modulePath = await loader.absolute('lodash');
const relativePath = loader.relative('./src/index.js', modulePath);
// Find all modules in a directory
const modules = await loader.modules('.');// Dynamically import plugins
const pluginPath = await loader.resolveFile(`./plugins/${name}`, ['.js', '.ts']);
if (pluginPath) {
const plugin = await loader.import(pluginPath, true);
// Use plugin.default
}