UNPKG

upath

Version:

A drop-in replacement / proxy to Node.js path, replacing \\ with / for all results & adding file extension functions.

1 lines 13.1 kB
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import * as path from 'node:path'\n\ndeclare const __UPATH_VERSION__: string\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isString(val: unknown): val is string {\n return typeof val === 'string'\n}\n\n/**\n * Convert all backslashes to forward slashes and collapse duplicate slashes\n * (except a leading double-slash for UNC paths).\n */\nfunction toUnix(p: string): string {\n p = p.replace(/\\\\/g, '/')\n p = p.replace(/(?<!^)\\/+/g, '/') // collapse duplicates, preserve leading // for UNC\n return p\n}\n\n// ---------------------------------------------------------------------------\n// Build the upath object via dynamic proxy over `path`\n// ---------------------------------------------------------------------------\n\n// The runtime object that collects everything. We type it loosely here and\n// provide precise exported bindings below for IDE / consumer use.\nconst upath: Record<string, unknown> = {}\n\n// Wrap every path function so its string arguments are unix-ified on the way\n// in and the string result is unix-ified on the way out. Non-function\n// properties (sep, delimiter) are copied as-is, except `posix` and `win32`\n// which are circular references and passed through without wrapping.\nfor (const [propName, propValue] of Object.entries(path)) {\n if (propName === 'posix' || propName === 'win32') {\n // Circular platform objects — pass through without wrapping\n upath[propName] = propValue\n } else if (typeof propValue === 'function') {\n upath[propName] = (...args: unknown[]): unknown => {\n const mapped = args.map((a) => (isString(a) ? toUnix(a) : a))\n const result = (propValue as Function).apply(path, mapped)\n return isString(result) ? toUnix(result) : result\n }\n } else {\n upath[propName] = propValue\n }\n}\n\n// Force unix separator\nupath.sep = '/'\n\n// ---------------------------------------------------------------------------\n// Extra functions (unique to upath, not in Node's path)\n// ---------------------------------------------------------------------------\n\nfunction isValidExt(ext: string, ignoreExts: string[] = [], maxSize: number): boolean {\n return (\n !!ext && ext.length <= maxSize && !ignoreExts.map((e) => (e && e[0] !== '.' ? '.' + e : e)).includes(ext)\n )\n}\n\n/**\n * Normalize a path, preserving a leading `./` if the original had one,\n * and preserving UNC `//` or `//./' prefixes.\n */\nfunction normalizeSafe(p: string): string {\n p = toUnix(p)\n let result: string = (upath.normalize as (p: string) => string)(p)\n if (p.startsWith('./') && !result.startsWith('./') && !result.startsWith('..')) {\n result = './' + result\n } else if (p.startsWith('//') && !result.startsWith('//')) {\n if (p.startsWith('//./')) {\n result = '//.' + result\n } else {\n result = '/' + result\n }\n }\n return result\n}\n\n/**\n * Like `normalizeSafe` but also trims a trailing slash (unless the path is\n * root `/`).\n */\nfunction normalizeTrim(p: string): string {\n p = normalizeSafe(p)\n if (p.endsWith('/') && p.length > 1) {\n return p.slice(0, -1)\n }\n return p\n}\n\n/**\n * Like `path.join` but preserves a leading `./` from the first segment and\n * preserves UNC `//` or `//./' prefixes.\n */\nfunction joinSafe(...segments: string[]): string {\n const result: string = (upath.join as (...p: string[]) => string)(...segments)\n if (segments.length > 0) {\n const p0 = toUnix(segments[0])\n if (p0.startsWith('./') && !result.startsWith('./') && !result.startsWith('..')) {\n return './' + result\n } else if (p0.startsWith('//') && !result.startsWith('//')) {\n if (p0.startsWith('//./')) {\n return '//.' + result\n } else {\n return '/' + result\n }\n }\n }\n return result\n}\n\n/**\n * Add an extension to `file` if it doesn't already end with it.\n */\nfunction addExt(file: string, ext: string): string {\n if (!ext) return file\n if (ext[0] !== '.') ext = '.' + ext\n return file + (file.endsWith(ext) ? '' : ext)\n}\n\n/**\n * Trim the extension from `filename` if it's a valid extension (not in\n * `ignoreExts` and not longer than `maxSize`).\n */\nfunction trimExt(filename: string, ignoreExts?: string[], maxSize?: number): string {\n const _maxSize = maxSize ?? 7\n const _ignoreExts = ignoreExts ?? []\n const oldExt = (upath.extname as (p: string) => string)(filename)\n if (isValidExt(oldExt, _ignoreExts, _maxSize)) {\n return filename.slice(0, filename.length - oldExt.length)\n }\n return filename\n}\n\n/**\n * Remove a specific extension from `filename`. If the file doesn't have\n * that extension, return it unchanged.\n */\nfunction removeExt(filename: string, ext: string): string {\n if (!ext) return filename\n ext = ext[0] === '.' ? ext : '.' + ext\n if ((upath.extname as (p: string) => string)(filename) === ext) {\n return trimExt(filename, [], ext.length)\n }\n return filename\n}\n\n/**\n * Change the extension of `filename`. The old extension is trimmed first\n * (subject to `ignoreExts` / `maxSize`), then `ext` is appended.\n */\nfunction changeExt(filename: string, ext: string, ignoreExts?: string[], maxSize?: number): string {\n const _maxSize = maxSize ?? 7\n const _ignoreExts = ignoreExts ?? []\n const trimmed = trimExt(filename, _ignoreExts, _maxSize)\n if (!ext) return trimmed\n return trimmed + (ext[0] === '.' ? ext : '.' + ext)\n}\n\n/**\n * Add `ext` to `filename` only when it doesn't already have a valid\n * extension (not in `ignoreExts` and not longer than `maxSize`).\n */\nfunction defaultExt(filename: string, ext: string, ignoreExts?: string[], maxSize?: number): string {\n const _maxSize = maxSize ?? 7\n const _ignoreExts = ignoreExts ?? []\n const oldExt = (upath.extname as (p: string) => string)(filename)\n if (isValidExt(oldExt, _ignoreExts, _maxSize)) {\n return filename\n }\n return addExt(filename, ext)\n}\n\n// Register extra functions on the runtime object\nconst extraFunctions: Record<string, Function> = {\n toUnix,\n normalizeSafe,\n normalizeTrim,\n joinSafe,\n addExt,\n trimExt,\n removeExt,\n changeExt,\n defaultExt,\n}\n\nfor (const [name, fn] of Object.entries(extraFunctions)) {\n if (upath[name] !== undefined) {\n throw new Error(`path.${name} already exists.`)\n }\n upath[name] = fn\n}\n\n// ---------------------------------------------------------------------------\n// Version\n// ---------------------------------------------------------------------------\n\nupath.VERSION = __UPATH_VERSION__\n\n// ---------------------------------------------------------------------------\n// Re-export path types for consumers\n// ---------------------------------------------------------------------------\n\nexport type { ParsedPath, FormatInputPathObject, PlatformPath } from 'node:path'\n\n// ---------------------------------------------------------------------------\n// Typed named exports — gives consumers IDE autocompletion + type safety\n// ---------------------------------------------------------------------------\n\n// Proxied path functions\nexport const resolve = upath.resolve as (...paths: string[]) => string\nexport const normalize = upath.normalize as (p: string) => string\nexport const isAbsolute = upath.isAbsolute as (p: string) => boolean\nexport const join = upath.join as (...paths: string[]) => string\nexport const relative = upath.relative as (from: string, to: string) => string\nexport const dirname = upath.dirname as (p: string) => string\nexport const basename = upath.basename as (p: string, suffix?: string) => string\nexport const extname = upath.extname as (p: string) => string\nexport const format = upath.format as (pathObject: path.FormatInputPathObject) => string\nexport const parse = upath.parse as (p: string) => path.ParsedPath\nexport const toNamespacedPath = upath.toNamespacedPath as (p: string) => string\n// matchesGlob was added in Node 22 — may be undefined on Node 20\nexport const matchesGlob = upath.matchesGlob as ((p: string, pattern: string) => boolean) | undefined\n\n// String properties\nexport const sep = upath.sep as '/'\nexport const delimiter = upath.delimiter as string\n\n// Platform objects (pass-through)\nexport const posix = upath.posix as path.PlatformPath\nexport const win32 = upath.win32 as path.PlatformPath\n\n// Version\nexport const VERSION = upath.VERSION as string\n\n// Extra functions (upath-only)\nexport { toUnix, normalizeSafe, normalizeTrim, joinSafe, addExt, trimExt, removeExt, changeExt, defaultExt }\n\n// ---------------------------------------------------------------------------\n// Default export — the full upath object, typed as the union of path + extras\n// ---------------------------------------------------------------------------\n\nexport interface UPath extends path.PlatformPath {\n VERSION: string\n sep: '/'\n toUnix(p: string): string\n normalizeSafe(p: string): string\n normalizeTrim(p: string): string\n joinSafe(...paths: string[]): string\n addExt(file: string, ext: string): string\n trimExt(filename: string, ignoreExts?: string[], maxSize?: number): string\n removeExt(filename: string, ext: string): string\n changeExt(filename: string, ext: string, ignoreExts?: string[], maxSize?: number): string\n defaultExt(filename: string, ext: string, ignoreExts?: string[], maxSize?: number): string\n}\n\nexport default upath as unknown as UPath\n"],"mappings":"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","names":[]}