DohPath is Doh's advanced path manipulation and resolution utility, engineered as a complete solution for reliable and consistent path management across the most demanding scenarios: diverse runtimes (Node.js, Browser), operating systems (Windows, Unix-like), project stages (development, exported VFS), and path formats. It transcends the limitations of standard path libraries by introducing a unique Layered Relativity system alongside a comprehensive suite of tools for normalization, resolution, storage, comparison, joining, and deconstruction.
Think of DohPath
not just as a replacement for Node.js path
, but as a foundational layer for managing location references throughout your Doh application lifecycle. It provides:
DohPath(path, context)
function seamlessly resolves various path formats (absolute, project-relative, context-relative, dot notation) to normalized, absolute system paths suitable for the current environment (Node/Browser) and OS, automatically handling separators../
, ../
) to offer:/
): Stable references relative to a global project root (LoadDohFrom
). Ideal for application-wide configurations or assets. Handled via DohPath.DohSlash()
.^/
): Flexible references relative to an arbitrary local file context (ArbitrarilyRelativePath
). Perfect for component-local assets or templates. Handled via DohPath.Caret()
.DohSlash
, Caret
, Dot
, Relative
) for consistent storage or usage.DohPath.Join()
) and deconstruction (DohPath.Basename()
, DohPath.Dirname()
) that work consistently everywhere.DohPath.Compare()
checks if two paths point to the same location, regardless of formatting differences (slashes, relativity types, trailing characters).doh export
, requiring no code changes for VFS compatibility.DohPath.Overload(context)
creates context-aware instances, reducing boilerplate when working within specific file scopes (like modules).This guide details these powerful functions and concepts, empowering you to implement robust, maintainable, and universally compatible path management in your most complex Doh applications.
DohPath
provides a comprehensive toolkit built around these fundamental ideas, addressing the full spectrum of path management needs:
The Universal Resolver (DohPath()
): The cornerstone function. Takes a path string (p
) and an optional context (ArbitrarilyRelativePath
). It intelligently deciphers the path type (/
, ^/
, absolute, ./
, etc.), resolves it against the appropriate reference (project root or local context), normalizes it for the current OS, and returns a definitive, absolute system path.
Layered Relativity (LoadDohFrom
vs. ArbitrarilyRelativePath
): The core innovation enabling flexible yet stable referencing.
LoadDohFrom
: A global setting defining the project's root directory. Acts as the anchor for project-relative DohSlash (/
) paths. Ensures consistent access to shared resources across the application.ArbitrarilyRelativePath
: The key to controlled, portable relativity. This isn't tied to unpredictable contexts like the Current Working Directory (CWD) or the immediate import.meta.url
of the runtime. Instead, it's an explicitly provided anchor (often a file path/URL, but can be any resolvable reference point you define) for context-relative Caret (^/
) paths. This decouples relative resolution from the environment's volatile defaults, solving the nightmare of tracking relative paths through complex workflows (e.g., browser -> server upload -> temp file processing). You control the reference point, ensuring predictable resolution regardless of where the code is running or how the path arrived there./config.json
) alongside the controlled, portable relativity of Caret paths (^/component-data.json
), essential for robust, maintainable applications operating across diverse environments and processing stages.Bi-Directional Path Format Conversion (DohPath.DohSlash
, DohPath.Caret
, DohPath.Dot
, DohPath.Relative
): These functions are not just resolvers; they allow you to enforce a specific path format onto any resolvable path. Need to store a path relative to the project root in a config file? Use DohPath.DohSlash(absoluteOrCaretPath)
. Need a traditional relative path for a third-party library? Use DohPath.Dot(anyResolvablePath, context)
. Need a path that maintains its relationship to a specific logical anchor, immune to environmental shifts? Use DohPath.Caret(anyPath, yourChosenAnchorPath)
. This ensures paths are stored and used in the required format consistently. (DohPath.DohSlash
is generally preferred for internal storage over DohPath.Relative
).
Robust Path Manipulation & Comparison: Beyond simple string operations, these tools understand path semantics:
DohPath.Join()
: Combines path segments reliably, ensuring correct separators, regardless of environment. Handles the base path and relative fragments correctly.DohPath.Basename()
, DohPath.Dirname()
: Extract file or directory names accurately, matching Node.js path
behavior but working cross-environment and handling edge cases like root paths correctly.DohPath.Compare()
: Essential for reliable checks. Determines if two differently formatted paths (e.g., /path/file
vs ^/..\\path\\file
from the right context, or paths with different slashes/trailing characters) actually refer to the same file system entity after resolution.Contextual Simplification (DohPath.Overload()
): Creates a specialized DohPath
instance pre-configured with a specific ArbitrarilyRelativePath
. This is invaluable within modules or specific file scopes, allowing you to use DohPath('^/local.file')
without repeatedly passing import.meta.url
, making code cleaner and less error-prone.
Transparent Export & VFS Handling: Designed with deployment in mind. When using doh export
, paths resolved via DohPath.DohSlash
(and potentially others if manually configured) are automatically recognized by the build process. These paths are then seamlessly mapped to the corresponding assets embedded within the exported application's virtual filesystem. Your application logic using DohPath
continues to work without modification, whether accessing the live filesystem or the VFS.
A global variable that defines the absolute path to your project root. This serves as the reference point for all /
-prefixed (DohSlash) paths and maps to the web root in browser environments.
A parameter passed to DohPath functions that defines the local context for resolving ^/
-prefixed (Caret) paths.
import.meta.url
or the current file's pathDohPath supports multiple path formats, each with different relativity references:
/config.json
): Relative to project root (LoadDohFrom
)^/styles.css
): Relative to the specified local context (ArbitrarilyRelativePath
)./styles.css
): Traditional relative paths (converted to/from Caret paths)/var/www/site/config.json
): Absolute system paths (We know the difference by matching inital path parts)This section serves as the API reference for the main DohPath functions.
Resolves a path to an absolute system path, accounting for both project root and local context references.
Syntax:
DohPath(p, ArbitrarilyRelativePath = null)
p
: The path to resolve. Can be:/
-prefixed for paths relative to LoadDohFrom
(project root)^/
-prefixed for paths relative to ArbitrarilyRelativePath
(local context)ArbitrarilyRelativePath
: The local context reference for resolving ^/
-prefixed paths (typically import.meta.url
)Returns: A fully resolved absolute system path.
Converts any path into a project-root-relative path (DohSlash format).
Syntax:
DohPath.DohSlash(p, ArbitrarilyRelativePath = null)
Returns: A DohSlash path string (starting with /
) that's relative to the project root.
Converts any path into an arbitrarily-relative path (Caret format, ^/
) that's relative to a specified, controlled local context, decoupling it from implicit environmental relativity (like CWD or script location).
Syntax:
DohPath.Caret(p, ArbitrarilyRelativePath = null)
p
: The path to convert. Can be absolute, DohSlash, Dot, or even another Caret path (relative to a different context).ArbitrarilyRelativePath
: The explicit anchor path/URL. This defines the meaning of ^/
for the conversion. If using an Overload
ed DohPath
, this defaults to the overloaded context.Returns: A Caret path string (starting with ^/
) whose relativity is precisely defined by the provided ArbitrarilyRelativePath
, making it portable and predictable across different execution environments or stages.
Converts a path into traditional dot notation format, replacing caret notation with dot notation.
Syntax:
DohPath.Dot(p, ArbitrarilyRelativePath = null)
Returns: A dot notation path string (starting with .
or ./
).
Converts a path into a system-relative path without leading slash.
Syntax:
DohPath.Relative(p, ArbitrarilyRelativePath = null)
Returns: A relative path string (no leading slash).
Compares two paths to determine if they resolve to the same location after normalization and resolution. It's more permissive than strict string equality, handling different slash types, trailing slashes, and different relativity types (/
, ^/
, etc.).
Syntax:
DohPath.Compare(p1, p2, ArbitrarilyRelativePath = null)
p1
: The first path to compare.p2
: The second path to compare.ArbitrarilyRelativePath
: The local context reference used if paths need resolution (e.g., comparing a Caret path to a DohSlash path).Returns: true
if the paths resolve to the same location, false
otherwise.
Joins multiple path segments together, similar to Node.js path.join
, but cross-environment compatible. Ensures correct separator usage.
Syntax:
DohPath.Join(p, ...fragments)
p
: The initial path segment.fragments
: Additional path segments to join. These must be relative path strings.Returns: A new path string formed by joining the segments. Throws an error if any fragment is not a relative path.
Returns the last portion of a path, similar to Node.js path.basename
.
Syntax:
DohPath.Basename(p)
p
: The path from which to extract the basename.Returns: The basename of the path. Handles different separators and trailing slashes. Returns /
for the root path.
Returns the directory name of a path, similar to Node.js path.dirname
.
Syntax:
DohPath.Dirname(p)
p
: The path from which to extract the directory name.Returns: The directory name of the path. Returns .
for paths without directory separators (implying the current directory) and /
for the root path.
Creates a specialized version of DohPath that has a pre-configured default value for ArbitrarilyRelativePath
.
Syntax:
DohPath.Overload(import_url)
Purpose:
ArbitrarilyRelativePath
with each callArbitrarilyRelativePath
Returns: A specialized DohPath function with all methods that automatically uses the provided URL as the default ArbitrarilyRelativePath
.
The recommended way to use DohPath is through Doh modules, where it's automatically provided with the correct local context:
Doh.Module('MyModule', ['dependency1'], function(DohPath) {
// DohPath is automatically Overloaded with this module's context
const configPath = DohPath('/config/settings.json'); // Uses LoadDohFrom (project root)
const localPath = DohPath('^/styles/local.css'); // Uses module file's path as ArbitrarilyRelativePath
});
DohPath integrates seamlessly with the Doh export tool, enabling exported applications to work with embedded assets through a virtual filesystem (VFS).
When an application is exported using doh export
, DohPath automatically checks the virtual filesystem before attempting to access the actual filesystem:
// In your application code:
const imagePath = DohPath.DohSlash('/assets/logo.png');
// Only .DohSlash is automatically detected by Auto-Packager to populate
// the VFS *for you*. You can populate the VFS too, ya know.
// In browser with exported application
// DohPath will first check DohOptions.VFS['/assets/logo.png']
// and return the embedded data URL if found
This virtual filesystem integration allows your application code to remain unchanged while working with both standard deployments and exported standalone applications.
The export tool maintains all path relationships by:
/
, ^/
, ./
) to their appropriate virtual locationsDohPath automatically recognizes and preserves data URLs, allowing them to pass through untouched:
// Data URLs are passed through unchanged
const dataUrl = "data:image/png;base64,iVBORw0KGg...";
DohPath(dataUrl) === dataUrl; // true
This enables the export tool to replace file paths with data URLs while ensuring all DohPath operations continue to work correctly.
// Global setup:
globalThis.LoadDohFrom = '/project/root';
// Using DohPath in different locations:
// In /project/root/modules/auth/login.js:
const DohPath = DohPath.Overload(import.meta.url);
// Paths relative to LoadDohFrom (project root):
const configPath = DohPath('/config/settings.json');
// Returns: '/project/root/config/settings.json'
// Paths relative to ArbitrarilyRelativePath (current file):
const stylePath = DohPath('^/styles/login.css');
// Returns: '/project/root/modules/auth/styles/login.css'
// Converting between relativity types:
const absolutePath = '/project/root/modules/auth/styles/login.css';
const asDohSlash = DohPath.DohSlash(absolutePath);
// Returns: '/modules/auth/styles/login.css' (relative to LoadDohFrom)
const asCaret = DohPath.Caret(absolutePath, import.meta.url);
// Returns: '^/styles/login.css' (relative to ArbitrarilyRelativePath)
DohSlash paths (/
) are ideal for resources that should resolve relative to your project root:
// assuming: globalThis.LoadDohFrom = '/project/root';
// Converting absolute paths to DohSlash format
const absolutePath = '/project/root/config/settings.json';
const dohSlashPath = DohPath.DohSlash(absolutePath);
// Returns: '/config/settings.json'
// Using DohSlash paths
const resolvedPath = DohPath(dohSlashPath);
// Returns: '/project/root/config/settings.json'
Caret paths (^/
) are for resources that should remain relative to their containing file:
// In file: /project/modules/auth/login.js
const DohPath = DohPath.Overload(import.meta.url);
// Using caret paths
const localPath = DohPath('^/styles/login.css');
// Returns: '/project/modules/auth/styles/login.css'
// Converting absolute paths to Caret format
const absolutePath = '/project/modules/auth/styles/login.css';
const caretPath = DohPath.Caret(absolutePath); // ArbitrarilyRelativePath comes from Overload
// Returns: '^/styles/login.css'
Traditional dot notation is available through the Dot method:
// In file: /project/modules/auth/login.js
const DohPath = DohPath.Overload(import.meta.url);
// Converting a path to dot notation
const absolutePath = '/project/modules/auth/styles/login.css';
const dotPath = DohPath.Dot(absolutePath);
// Returns: './styles/login.css'
// In file: /var/www/project/components/navbar/index.js
const DohPath = DohPath.Overload(import.meta.url);
// Now all caret paths resolve relative to the navbar directory
const templatePath = DohPath('^/template.html');
// Returns: '/var/www/project/components/navbar/template.html'
// You can create another specialized DohPath with a different context
const utilsPath = '/var/www/project/utils/helpers.js';
const UtilsDohPath = DohPath.Overload(utilsPath);
// Now UtilsDohPath resolves caret paths relative to the utils directory
const helperPath = UtilsDohPath('^/validation.js');
// Returns: '/var/www/project/utils/validation.js'
// You can still override the ArbitrarilyRelativePath
const otherPath = DohPath('^/config.json', '/var/www/project/config/index.js');
// Returns: '/var/www/project/config/config.json'
DohPath normalizes paths across different platforms:
// Windows-style path
const windowsPath = 'C:\\project\\root\\config\\settings.json';
const normalizedPath = DohPath(windowsPath);
// Returns: 'C:/project/root/config/settings.json'
// In file: /project/modules/user/profile.js
const DohPath = DohPath.Overload(import.meta.url);
const path1 = '/project/modules/user/avatar.png';
const path2 = DohPath('^/avatar.png'); // Resolves to the same absolute path
const path3 = './avatar.png'; // Interpreted relative to cwd by default
const path4 = DohPath.DohSlash(path1); // '/modules/user/avatar.png'
DohPath.Compare(path1, path2); // true (Absolute vs Caret)
DohPath.Compare(path2, DohPath.Dot(path2)); // true (Caret vs Dot, resolved relative to profile.js)
DohPath.Compare(path1, path4); // true (Absolute vs DohSlash)
DohPath.Compare('/path/to/file', '/path/to/file/'); // true (Trailing slash ignored)
const basePath = '/project/assets';
const joinedPath = DohPath.Join(basePath, 'images', 'icons', 'favicon.ico');
// Returns: '/project/assets/images/icons/favicon.ico'
const relativeJoin = DohPath.Join('^/data', '../common', 'config.json');
// Returns: '^/../common/config.json' (Join doesn't resolve '..', DohPath() does)
// Throws an error because '/absolute/fragment' is not relative:
// DohPath.Join(basePath, '/absolute/fragment');
const filePath = '/project/src/utils/helpers.js';
DohPath.Basename(filePath); // Returns: 'helpers.js'
DohPath.Dirname(filePath); // Returns: '/project/src/utils'
DohPath.Basename('/project/src/'); // Returns: 'src'
DohPath.Dirname('/project/src/'); // Returns: '/project'
DohPath.Basename('/'); // Returns: '/'
DohPath.Dirname('/'); // Returns: '/'
DohPath.Basename('file.txt'); // Returns: 'file.txt'
DohPath.Dirname('file.txt'); // Returns: '.'
// For storing paths in configuration:
const absolutePath = '/project/root/assets/images/logo.png';
const storedPath = DohPath.DohSlash(absolutePath);
// Store as: '/assets/images/logo.png'
// For module-specific references:
const moduleContext = '/project/root/modules/profile/index.js';
const moduleRelativePath = DohPath.Caret(absolutePath, moduleContext);
// Store as: '^/../../assets/images/logo.png'
// For traditional system compatibility:
const traditionalPath = DohPath.Dot(absolutePath, moduleContext);
// Store as: './../../assets/images/logo.png'
Use DohSlash for Project-Wide Resources:
const configPath = DohPath('/config/app.json'); // Relative to LoadDohFrom
Use Caret for Module-Local Resources:
const stylePath = DohPath('^/styles/component.css'); // Relative to ArbitrarilyRelativePath
Prefer Module Integration:
Doh.Module('MyModule', function(DohPath) {
// DohPath is already Overloaded with this module's context
const configPath = DohPath('/config/settings.json');
const localPath = DohPath('^/styles/local.css');
});
For Standalone Scripts, Use Overload:
const DohPath = DohPath.Overload(import.meta.url);
// Now you can use DohPath without specifying ArbitrarilyRelativePath
For Path Storage:
LoadDohFrom
is a global variable that defines the project root and serves as the reference point for all /
-prefixed paths. ArbitrarilyRelativePath
is a parameter that defines the local context for resolving ^/
-prefixed paths, typically set to the current file's path.
Use DohSlash (/
) for paths that should resolve from your project root and remain consistent project-wide. Use Caret (^/
) for paths that need to maintain relativity to their containing file, especially useful for components or modules that might be moved.
Overload creates a specialized DohPath instance with a pre-configured default context, eliminating the need to repeatedly pass the same context. It's a drop-in replacement that maintains all the original functionality while adding a default context.
Yes, it's safe to call Overload on an already-overloaded DohPath. Each call creates a new specialized instance with the updated context, allowing for flexible and layered context management.
Caret notation (^/
) is Doh-specific and designed to maintain arbitrarily-relative paths without interference from automatic path resolvers. Dot notation (./ or ../
) is the traditional format used in most JavaScript systems but can be problematic when paths are resolved automatically in different contexts. Caret paths allow explicit control over relativity.
The layered system allows you to: