# 👽 ufo [![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![bundle][bundle-src]][bundle-href] [![Codecov][codecov-src]][codecov-href] [![License][license-src]][license-href] [![JSDocs][jsdocs-src]][jsdocs-href] URL utils for humans. ## Install Install using npm or your favourite package manager: ```bash npm i ufo ``` Import: ```js // CommonJS const { normalizeURL, joinURL } = require('ufo') // ESM import { normalizeURL, joinURL } from 'ufo' // Deno import { parseURL } from 'https://unpkg.com/ufo/dist/index.mjs' ``` **Notice:** You may need to transpile package and add URL polyfill for legacy environments ## Usage ### `normalizeURL` - Ensures URL is properly encoded - Ensures pathname starts with slash - Preserves protocol/host if provided ```ts normalizeURL('test?query=123 123#hash, test') // test?query=123%20123#hash,%20test normalizeURL('http://localhost:3000') // http://localhost:3000/ ``` ### `joinURL` ```ts joinURL('a', '/b', '/c') // a/b/c ``` ### `resolveURL` ```ts resolveURL('http://foo.com/foo?test=123#token', 'bar', 'baz') // http://foo.com/foo/bar/baz?test=123#token ``` ### `parseURL` ```ts parseURL('http://foo.com/foo?test=123#token') // { protocol: 'http:', auth: '', host: 'foo.com', pathname: '/foo', search: '?test=123', hash: '#token' } parseURL('foo.com/foo?test=123#token') // { pathname: 'foo.com/foo', search: '?test=123', hash: '#token' } parseURL('foo.com/foo?test=123#token', 'https://') // { protocol: 'https:', auth: '', host: 'foo.com', pathname: '/foo', search: '?test=123', hash: '#token' } ``` ### `stringifyParsedURL` ```ts const obj = parseURL('http://foo.com/foo?test=123#token') obj.host = 'bar.com' stringifyParsedURL(obj) // http://bar.com/foo?test=123#token ``` ### `withQuery` ```ts withQuery('/foo?page=a', { token: 'secret' }) // /foo?page=a&token=secret ``` ### `getQuery` ```ts getQuery('http://foo.com/foo?test=123&unicode=%E5%A5%BD') // { test: '123', unicode: '好' } ``` ### `parseFilename` ```ts // Result: filename.ext parseFilename('http://example.com/path/to/filename.ext') // Result: undefined parseFilename('/path/to/.hidden-file', { strict: true }) ``` ### `$URL` Implementing URL interface with improvements: - Supporting schemeless and hostless URLs - Supporting relative URLs - Preserving trailing-slash status - Decoded and mutable class properties (`protocol`, `host`, `auth`, `pathname`, `query`, `hash`) - Consistent URL parser independent of environment - Consistent encoding independent of environment - Punycode support for host encoding ```ts new $URL('http://localhost:3000/hello?world=true') // { protocol: 'http:', host: 'localhost:3000', auth: '', pathname: '/hello', query: { world: 'true' }, hash: '' } ``` ### `withTrailingSlash` Ensures url ends with a trailing slash. ```ts withTrailingSlash('/foo') // /foo/ ``` Set the second option to `true` to support query parameters: ```ts withTrailingSlash('/path?query=true', true) // /path/?query=true ``` ### `withoutTrailingSlash` Ensures url does not ends with a trailing slash. ```ts withoutTrailingSlash('/foo/') // /foo ``` Set the second option to `true` to support query parameters: ```ts withoutTrailingSlash('/path/?query=true', true) // /path?query=true ``` ### `cleanDoubleSlashes` Ensures url does not have double slash (except for protocol). ```ts cleanDoubleSlashes('//foo//bar//') // /foo/bar/ cleanDoubleSlashes('http://example.com/analyze//http://localhost:3000//') // http://example.com/analyze/http://localhost:3000/ ``` ### `isSamePath` Check two paths are equal or not. Trailing slash and encoding are normalized before comparison. ```ts isSamePath('/foo', '/foo/') // true ``` ### `isRelative` Check if a path starts with `./` or `../`. ```ts isRelative('./foo') // true ``` ### `withHttp` Ensures url protocol is `http` ```ts withHttp('https://example.com') // http://example.com ``` ### `withHttps` Ensures url protocol is `https` ```ts withHttps('http://example.com') // https://example.com ``` ### `withProtocol` Changes url protocol passed as second argument ```ts withProtocol('http://example.com', 'ftp://') // ftp://example.com ``` ### `withoutProtocol` Removes url protocol ```ts withoutProtocol('http://example.com') // example.com ``` ### `isEqual` Compare two URLs regardless of their slash condition or encoding: ```ts isEqual('/foo', 'foo') // true isEqual('foo/', 'foo') // true isEqual('/foo bar', '/foo%20bar') // true // Strict compare isEqual('/foo', 'foo', { leadingSlash: true }) // false isEqual('foo/', 'foo', { trailingSlash: true }) // false isEqual('/foo bar', '/foo%20bar', { encoding: true }) // false ``` ## License [MIT](./LICENSE) Special thanks to Eduardo San Martin Morote ([posva](https://github.com/posva)) for [encoding utilities](https://github.com/vuejs/vue-router-next/blob/v4.0.1/src/encoding.ts) [npm-version-src]: https://img.shields.io/npm/v/ufo?style=flat&colorA=18181B&colorB=F0DB4F [npm-version-href]: https://npmjs.com/package/ufo [npm-downloads-src]: https://img.shields.io/npm/dm/ufo?style=flat&colorA=18181B&colorB=F0DB4F [npm-downloads-href]: https://npmjs.com/package/ufo [codecov-src]: https://img.shields.io/codecov/c/gh/unjs/ufo/main?style=flat&colorA=18181B&colorB=F0DB4F [codecov-href]: https://codecov.io/gh/unjs/ufo [bundle-src]: https://img.shields.io/bundlephobia/minzip/ufo?style=flat&colorA=18181B&colorB=F0DB4F [bundle-href]: https://bundlephobia.com/result?p=ufo [license-src]: https://img.shields.io/github/license/unjs/ufo.svg?style=flat&colorA=18181B&colorB=F0DB4F [license-href]: https://github.com/unjs/ufo/blob/main/LICENSE [jsdocs-src]: https://img.shields.io/badge/jsDocs.io-reference-18181B?style=flat&colorA=18181B&colorB=F0DB4F [jsdocs-href]: https://www.jsdocs.io/package/ufo