From c93b4aee6e170481827baf5062c12f3137f73c6a Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Wed, 3 Aug 2022 12:25:08 -0400 Subject: [PATCH 1/7] fix: cjs to load normalize-url --- dist/index.js | 251 +++++++++++++++++++++++++++++++++++++++++++++- dist/index.mjs | 248 ++++++++++++++++++++++++++++++++++++++++++++- package-lock.json | 1 - package.json | 1 - 4 files changed, 495 insertions(+), 6 deletions(-) diff --git a/dist/index.js b/dist/index.js index d9100ec..4c0f27e 100755 --- a/dist/index.js +++ b/dist/index.js @@ -1,12 +1,257 @@ 'use strict'; var parsePath = require('parse-path'); -var normalizeUrl = require('normalize-url'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var parsePath__default = /*#__PURE__*/_interopDefaultLegacy(parsePath); -var normalizeUrl__default = /*#__PURE__*/_interopDefaultLegacy(normalizeUrl); + +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs +const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain'; +const DATA_URL_DEFAULT_CHARSET = 'us-ascii'; + +const testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name); + +const normalizeDataURL = (urlString, {stripHash}) => { + const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString); + + if (!match) { + throw new Error(`Invalid URL: ${urlString}`); + } + + let {type, data, hash} = match.groups; + const mediaType = type.split(';'); + hash = stripHash ? '' : hash; + + let isBase64 = false; + if (mediaType[mediaType.length - 1] === 'base64') { + mediaType.pop(); + isBase64 = true; + } + + // Lowercase MIME type + const mimeType = (mediaType.shift() || '').toLowerCase(); + const attributes = mediaType + .map(attribute => { + let [key, value = ''] = attribute.split('=').map(string => string.trim()); + + // Lowercase `charset` + if (key === 'charset') { + value = value.toLowerCase(); + + if (value === DATA_URL_DEFAULT_CHARSET) { + return ''; + } + } + + return `${key}${value ? `=${value}` : ''}`; + }) + .filter(Boolean); + + const normalizedMediaType = [ + ...attributes, + ]; + + if (isBase64) { + normalizedMediaType.push('base64'); + } + + if (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) { + normalizedMediaType.unshift(mimeType); + } + + return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`; +}; + +function normalizeUrl(urlString, options) { + options = { + defaultProtocol: 'http:', + normalizeProtocol: true, + forceHttp: false, + forceHttps: false, + stripAuthentication: true, + stripHash: false, + stripTextFragment: true, + stripWWW: true, + removeQueryParameters: [/^utm_\w+/i], + removeTrailingSlash: true, + removeSingleSlash: true, + removeDirectoryIndex: false, + sortQueryParameters: true, + ...options, + }; + + urlString = urlString.trim(); + + // Data URL + if (/^data:/i.test(urlString)) { + return normalizeDataURL(urlString, options); + } + + if (/^view-source:/i.test(urlString)) { + throw new Error('`view-source:` is not supported as it is a non-standard protocol'); + } + + const hasRelativeProtocol = urlString.startsWith('//'); + const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString); + + // Prepend protocol + if (!isRelativeUrl) { + urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol); + } + + const urlObject = new URL(urlString); + + if (options.forceHttp && options.forceHttps) { + throw new Error('The `forceHttp` and `forceHttps` options cannot be used together'); + } + + if (options.forceHttp && urlObject.protocol === 'https:') { + urlObject.protocol = 'http:'; + } + + if (options.forceHttps && urlObject.protocol === 'http:') { + urlObject.protocol = 'https:'; + } + + // Remove auth + if (options.stripAuthentication) { + urlObject.username = ''; + urlObject.password = ''; + } + + // Remove hash + if (options.stripHash) { + urlObject.hash = ''; + } else if (options.stripTextFragment) { + urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, ''); + } + + // Remove duplicate slashes if not preceded by a protocol + // NOTE: This could be implemented using a single negative lookbehind + // regex, but we avoid that to maintain compatibility with older js engines + // which do not have support for that feature. + if (urlObject.pathname) { + // TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(? 0) { + let pathComponents = urlObject.pathname.split('/'); + const lastComponent = pathComponents[pathComponents.length - 1]; + + if (testParameter(lastComponent, options.removeDirectoryIndex)) { + pathComponents = pathComponents.slice(0, -1); + urlObject.pathname = pathComponents.slice(1).join('/') + '/'; + } + } + + if (urlObject.hostname) { + // Remove trailing dot + urlObject.hostname = urlObject.hostname.replace(/\.$/, ''); + + // Remove `www.` + if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) { + // Each label should be max 63 at length (min: 1). + // Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names + // Each TLD should be up to 63 characters long (min: 2). + // It is technically possible to have a single character TLD, but none currently exist. + urlObject.hostname = urlObject.hostname.replace(/^www\./, ''); + } + } + + // Remove query unwanted parameters + if (Array.isArray(options.removeQueryParameters)) { + // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy. + for (const key of [...urlObject.searchParams.keys()]) { + if (testParameter(key, options.removeQueryParameters)) { + urlObject.searchParams.delete(key); + } + } + } + + if (options.removeQueryParameters === true) { + urlObject.search = ''; + } + + // Sort query parameters + if (options.sortQueryParameters) { + urlObject.searchParams.sort(); + + // Calling `.sort()` encodes the search parameters, so we need to decode them again. + try { + urlObject.search = decodeURIComponent(urlObject.search); + } catch {} + } + + if (options.removeTrailingSlash) { + urlObject.pathname = urlObject.pathname.replace(/\/$/, ''); + } + + const oldUrlString = urlString; + + // Take advantage of many of the Node `url` normalizations + urlString = urlObject.toString(); + + if (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') { + urlString = urlString.replace(/\/$/, ''); + } + + // Remove ending `/` unless removeSingleSlash is false + if ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) { + urlString = urlString.replace(/\/$/, ''); + } + + // Restore relative protocol, if applicable + if (hasRelativeProtocol && !options.normalizeProtocol) { + urlString = urlString.replace(/^http:\/\//, '//'); + } + + // Remove http/https + if (options.stripProtocol) { + urlString = urlString.replace(/^(?:https?:)?\/\//, ''); + } + + return urlString; +} // Dependencies @@ -65,7 +310,7 @@ const parseUrl = (url, normalize = false) => { stripHash: false }; } - url = normalizeUrl__default["default"](url, normalize); + url = normalizeUrl(url, normalize); } const parsed = parsePath__default["default"](url); diff --git a/dist/index.mjs b/dist/index.mjs index 0e255fd..f18c9e3 100755 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -1,5 +1,251 @@ import parsePath from 'parse-path'; -import normalizeUrl from 'normalize-url'; + +// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs +const DATA_URL_DEFAULT_MIME_TYPE = 'text/plain'; +const DATA_URL_DEFAULT_CHARSET = 'us-ascii'; + +const testParameter = (name, filters) => filters.some(filter => filter instanceof RegExp ? filter.test(name) : filter === name); + +const normalizeDataURL = (urlString, {stripHash}) => { + const match = /^data:(?[^,]*?),(?[^#]*?)(?:#(?.*))?$/.exec(urlString); + + if (!match) { + throw new Error(`Invalid URL: ${urlString}`); + } + + let {type, data, hash} = match.groups; + const mediaType = type.split(';'); + hash = stripHash ? '' : hash; + + let isBase64 = false; + if (mediaType[mediaType.length - 1] === 'base64') { + mediaType.pop(); + isBase64 = true; + } + + // Lowercase MIME type + const mimeType = (mediaType.shift() || '').toLowerCase(); + const attributes = mediaType + .map(attribute => { + let [key, value = ''] = attribute.split('=').map(string => string.trim()); + + // Lowercase `charset` + if (key === 'charset') { + value = value.toLowerCase(); + + if (value === DATA_URL_DEFAULT_CHARSET) { + return ''; + } + } + + return `${key}${value ? `=${value}` : ''}`; + }) + .filter(Boolean); + + const normalizedMediaType = [ + ...attributes, + ]; + + if (isBase64) { + normalizedMediaType.push('base64'); + } + + if (normalizedMediaType.length > 0 || (mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE)) { + normalizedMediaType.unshift(mimeType); + } + + return `data:${normalizedMediaType.join(';')},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ''}`; +}; + +function normalizeUrl(urlString, options) { + options = { + defaultProtocol: 'http:', + normalizeProtocol: true, + forceHttp: false, + forceHttps: false, + stripAuthentication: true, + stripHash: false, + stripTextFragment: true, + stripWWW: true, + removeQueryParameters: [/^utm_\w+/i], + removeTrailingSlash: true, + removeSingleSlash: true, + removeDirectoryIndex: false, + sortQueryParameters: true, + ...options, + }; + + urlString = urlString.trim(); + + // Data URL + if (/^data:/i.test(urlString)) { + return normalizeDataURL(urlString, options); + } + + if (/^view-source:/i.test(urlString)) { + throw new Error('`view-source:` is not supported as it is a non-standard protocol'); + } + + const hasRelativeProtocol = urlString.startsWith('//'); + const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString); + + // Prepend protocol + if (!isRelativeUrl) { + urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol); + } + + const urlObject = new URL(urlString); + + if (options.forceHttp && options.forceHttps) { + throw new Error('The `forceHttp` and `forceHttps` options cannot be used together'); + } + + if (options.forceHttp && urlObject.protocol === 'https:') { + urlObject.protocol = 'http:'; + } + + if (options.forceHttps && urlObject.protocol === 'http:') { + urlObject.protocol = 'https:'; + } + + // Remove auth + if (options.stripAuthentication) { + urlObject.username = ''; + urlObject.password = ''; + } + + // Remove hash + if (options.stripHash) { + urlObject.hash = ''; + } else if (options.stripTextFragment) { + urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, ''); + } + + // Remove duplicate slashes if not preceded by a protocol + // NOTE: This could be implemented using a single negative lookbehind + // regex, but we avoid that to maintain compatibility with older js engines + // which do not have support for that feature. + if (urlObject.pathname) { + // TODO: Replace everything below with `urlObject.pathname = urlObject.pathname.replace(/(? 0) { + let pathComponents = urlObject.pathname.split('/'); + const lastComponent = pathComponents[pathComponents.length - 1]; + + if (testParameter(lastComponent, options.removeDirectoryIndex)) { + pathComponents = pathComponents.slice(0, -1); + urlObject.pathname = pathComponents.slice(1).join('/') + '/'; + } + } + + if (urlObject.hostname) { + // Remove trailing dot + urlObject.hostname = urlObject.hostname.replace(/\.$/, ''); + + // Remove `www.` + if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) { + // Each label should be max 63 at length (min: 1). + // Source: https://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names + // Each TLD should be up to 63 characters long (min: 2). + // It is technically possible to have a single character TLD, but none currently exist. + urlObject.hostname = urlObject.hostname.replace(/^www\./, ''); + } + } + + // Remove query unwanted parameters + if (Array.isArray(options.removeQueryParameters)) { + // eslint-disable-next-line unicorn/no-useless-spread -- We are intentionally spreading to get a copy. + for (const key of [...urlObject.searchParams.keys()]) { + if (testParameter(key, options.removeQueryParameters)) { + urlObject.searchParams.delete(key); + } + } + } + + if (options.removeQueryParameters === true) { + urlObject.search = ''; + } + + // Sort query parameters + if (options.sortQueryParameters) { + urlObject.searchParams.sort(); + + // Calling `.sort()` encodes the search parameters, so we need to decode them again. + try { + urlObject.search = decodeURIComponent(urlObject.search); + } catch {} + } + + if (options.removeTrailingSlash) { + urlObject.pathname = urlObject.pathname.replace(/\/$/, ''); + } + + const oldUrlString = urlString; + + // Take advantage of many of the Node `url` normalizations + urlString = urlObject.toString(); + + if (!options.removeSingleSlash && urlObject.pathname === '/' && !oldUrlString.endsWith('/') && urlObject.hash === '') { + urlString = urlString.replace(/\/$/, ''); + } + + // Remove ending `/` unless removeSingleSlash is false + if ((options.removeTrailingSlash || urlObject.pathname === '/') && urlObject.hash === '' && options.removeSingleSlash) { + urlString = urlString.replace(/\/$/, ''); + } + + // Restore relative protocol, if applicable + if (hasRelativeProtocol && !options.normalizeProtocol) { + urlString = urlString.replace(/^http:\/\//, '//'); + } + + // Remove http/https + if (options.stripProtocol) { + urlString = urlString.replace(/^(?:https?:)?\/\//, ''); + } + + return urlString; +} // Dependencies diff --git a/package-lock.json b/package-lock.json index 67316d3..8d16f7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "8.0.0", "license": "MIT", "dependencies": { - "normalize-url": "^7.0.3", "parse-path": "^7.0.0" }, "devDependencies": { diff --git a/package.json b/package.json index 15a998b..1c9425f 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,6 @@ "normalize-url": "^7.0.3" }, "dependencies": { - "normalize-url": "^7.0.3", "parse-path": "^7.0.0" }, "files": [ From c2735c9fb3cd31c5261d07464bc08f7bb706fc21 Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Wed, 3 Aug 2022 12:30:35 -0400 Subject: [PATCH 2/7] test: use cjs file --- package.json | 4 ++-- test/index.mjs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1c9425f..e7a1745 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,9 @@ }, "homepage": "https://github.com/IonicaBizau/parse-url", "devDependencies": { + "normalize-url": "^7.0.3", "pkgroll": "^1.4.0", - "tester": "^1.3.1", - "normalize-url": "^7.0.3" + "tester": "^1.3.1" }, "dependencies": { "parse-path": "^7.0.0" diff --git a/test/index.mjs b/test/index.mjs index 7b484ba..0bb6ead 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -1,5 +1,5 @@ // Dependencies -import parseUrl from "../dist/index.mjs"; +import parseUrl from "../dist/index.js"; import tester from "tester"; import normalizeUrl from "normalize-url"; From 881ecb46e39286b0c2b3c32fe61dca9377176884 Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Wed, 3 Aug 2022 13:00:16 -0400 Subject: [PATCH 3/7] feat: improve regex pattern --- dist/index.js | 11 ++++++----- dist/index.mjs | 11 ++++++----- src/index.js | 11 ++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/dist/index.js b/dist/index.js index d9100ec..8b54fee 100755 --- a/dist/index.js +++ b/dist/index.js @@ -43,7 +43,7 @@ var normalizeUrl__default = /*#__PURE__*/_interopDefaultLegacy(normalizeUrl); const parseUrl = (url, normalize = false) => { // Constants - const GIT_RE = /(^(git@|http(s)?:\/\/)([\w\.\-@]+)(\/|:))(([\~,\.\w,\-,\_,\/]+)(.git){0,1}((\/){0,1}))/; + const GIT_RE = /^(?:git@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/; const throwErr = msg => { const err = new Error(msg); @@ -72,14 +72,15 @@ const parseUrl = (url, normalize = false) => { // Potential git-ssh urls if (parsed.parse_failed) { - const matched = parsed.href.match(GIT_RE); + const matched = parsed.href.match(GIT_RE); + if (matched) { parsed.protocols = ["ssh"]; parsed.protocol = "ssh"; - parsed.resource = matched[4]; - parsed.host = matched[4]; + parsed.resource = matched[1]; + parsed.host = matched[1]; parsed.user = "git"; - parsed.pathname = `/${matched[6]}`; + parsed.pathname = `/${matched[2]}`; parsed.parse_failed = false; } else { throwErr("URL parsing failed."); diff --git a/dist/index.mjs b/dist/index.mjs index 0e255fd..40f37d6 100755 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -36,7 +36,7 @@ import normalizeUrl from 'normalize-url'; const parseUrl = (url, normalize = false) => { // Constants - const GIT_RE = /(^(git@|http(s)?:\/\/)([\w\.\-@]+)(\/|:))(([\~,\.\w,\-,\_,\/]+)(.git){0,1}((\/){0,1}))/; + const GIT_RE = /^(?:git@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/; const throwErr = msg => { const err = new Error(msg); @@ -65,14 +65,15 @@ const parseUrl = (url, normalize = false) => { // Potential git-ssh urls if (parsed.parse_failed) { - const matched = parsed.href.match(GIT_RE); + const matched = parsed.href.match(GIT_RE); + if (matched) { parsed.protocols = ["ssh"]; parsed.protocol = "ssh"; - parsed.resource = matched[4]; - parsed.host = matched[4]; + parsed.resource = matched[1]; + parsed.host = matched[1]; parsed.user = "git"; - parsed.pathname = `/${matched[6]}`; + parsed.pathname = `/${matched[2]}`; parsed.parse_failed = false; } else { throwErr("URL parsing failed."); diff --git a/src/index.js b/src/index.js index 3981b89..d7d9636 100644 --- a/src/index.js +++ b/src/index.js @@ -35,7 +35,7 @@ import normalizeUrl from "normalize-url"; const parseUrl = (url, normalize = false) => { // Constants - const GIT_RE = /(^(git@|http(s)?:\/\/)([\w\.\-@]+)(\/|:))(([\~,\.\w,\-,\_,\/]+)(.git){0,1}((\/){0,1}))/ + const GIT_RE = /^(?:git@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/ const throwErr = msg => { const err = new Error(msg) @@ -64,14 +64,15 @@ const parseUrl = (url, normalize = false) => { // Potential git-ssh urls if (parsed.parse_failed) { - const matched = parsed.href.match(GIT_RE) + const matched = parsed.href.match(GIT_RE) + if (matched) { parsed.protocols = ["ssh"] parsed.protocol = "ssh" - parsed.resource = matched[4] - parsed.host = matched[4] + parsed.resource = matched[1] + parsed.host = matched[1] parsed.user = "git" - parsed.pathname = `/${matched[6]}` + parsed.pathname = `/${matched[2]}` parsed.parse_failed = false } else { throwErr("URL parsing failed.") From 9500430a3b9973bb1b5b2b9b319af2685ad272b3 Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Wed, 3 Aug 2022 13:09:55 -0400 Subject: [PATCH 4/7] feat: support custom user in ssh url --- dist/index.js | 10 +++++----- dist/index.mjs | 10 +++++----- src/index.js | 10 +++++----- test/index.mjs | 17 +++++++++++++++++ 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/dist/index.js b/dist/index.js index 8b54fee..7bf5818 100755 --- a/dist/index.js +++ b/dist/index.js @@ -43,7 +43,7 @@ var normalizeUrl__default = /*#__PURE__*/_interopDefaultLegacy(normalizeUrl); const parseUrl = (url, normalize = false) => { // Constants - const GIT_RE = /^(?:git@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/; + const GIT_RE = /^(?:([a-z_][a-z0-9_-]{0,31})@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/; const throwErr = msg => { const err = new Error(msg); @@ -77,10 +77,10 @@ const parseUrl = (url, normalize = false) => { if (matched) { parsed.protocols = ["ssh"]; parsed.protocol = "ssh"; - parsed.resource = matched[1]; - parsed.host = matched[1]; - parsed.user = "git"; - parsed.pathname = `/${matched[2]}`; + parsed.resource = matched[2]; + parsed.host = matched[2]; + parsed.user = matched[1]; + parsed.pathname = `/${matched[3]}`; parsed.parse_failed = false; } else { throwErr("URL parsing failed."); diff --git a/dist/index.mjs b/dist/index.mjs index 40f37d6..eea37a1 100755 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -36,7 +36,7 @@ import normalizeUrl from 'normalize-url'; const parseUrl = (url, normalize = false) => { // Constants - const GIT_RE = /^(?:git@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/; + const GIT_RE = /^(?:([a-z_][a-z0-9_-]{0,31})@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/; const throwErr = msg => { const err = new Error(msg); @@ -70,10 +70,10 @@ const parseUrl = (url, normalize = false) => { if (matched) { parsed.protocols = ["ssh"]; parsed.protocol = "ssh"; - parsed.resource = matched[1]; - parsed.host = matched[1]; - parsed.user = "git"; - parsed.pathname = `/${matched[2]}`; + parsed.resource = matched[2]; + parsed.host = matched[2]; + parsed.user = matched[1]; + parsed.pathname = `/${matched[3]}`; parsed.parse_failed = false; } else { throwErr("URL parsing failed."); diff --git a/src/index.js b/src/index.js index d7d9636..b165cc7 100644 --- a/src/index.js +++ b/src/index.js @@ -35,7 +35,7 @@ import normalizeUrl from "normalize-url"; const parseUrl = (url, normalize = false) => { // Constants - const GIT_RE = /^(?:git@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/ + const GIT_RE = /^(?:([a-z_][a-z0-9_-]{0,31})@|https?:\/\/)([\w\.\-@]+)[\/:]([\~,\.\w,\-,\_,\/]+?(?:\.git|\/)?)$/ const throwErr = msg => { const err = new Error(msg) @@ -69,10 +69,10 @@ const parseUrl = (url, normalize = false) => { if (matched) { parsed.protocols = ["ssh"] parsed.protocol = "ssh" - parsed.resource = matched[1] - parsed.host = matched[1] - parsed.user = "git" - parsed.pathname = `/${matched[2]}` + parsed.resource = matched[2] + parsed.host = matched[2] + parsed.user = matched[1] + parsed.pathname = `/${matched[3]}` parsed.parse_failed = false } else { throwErr("URL parsing failed.") diff --git a/test/index.mjs b/test/index.mjs index 7b484ba..5c76aeb 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -133,6 +133,23 @@ const INPUTS = [ , parse_failed: false } ] + , [ + ["org-12345678@github.my-enterprise.com:my-org/my-repo.git", false], + { + protocols: [ 'ssh' ] + , protocol: 'ssh' + , port: '' + , resource: 'github.my-enterprise.com' + , host: 'github.my-enterprise.com' + , user: 'org-12345678' + , password: '' + , pathname: '/my-org/my-repo.git' + , hash: '' + , search: '' + , query: {} + , parse_failed: false + } + ] , [ ["git@github.com:halup/Cloud.API.Gateway.git", false] , { From 18831365e1b30d1ea6d008b7b2dd0efd929fd316 Mon Sep 17 00:00:00 2001 From: Brian Coit Date: Sat, 6 Aug 2022 01:21:14 +0100 Subject: [PATCH 5/7] Include index.d.ts in package Fixes issue in version 8 where index.d.ts is not included in package. --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 15a998b..ccbaf24 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "menu/", "cli.js", "index.js", + "index.d.ts", "bloggify.js", "bloggify.json", "bloggify/" From 9cacf38de02db0fb1358bd6ec04543e523cd6a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionic=C4=83=20Biz=C4=83u?= Date: Tue, 30 Aug 2022 15:45:28 +0300 Subject: [PATCH 6/7] :arrow_up: 8.1.0 :tada: --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 15a998b..7a00ed5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "parse-url", - "version": "8.0.0", + "version": "8.1.0", "description": "An advanced url parser supporting git urls too.", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -64,4 +64,4 @@ "For low-level path parsing, check out [`parse-path`](https://github.com/IonicaBizau/parse-path). This very module is designed to parse urls. By default the urls are normalized." ] } -} +} \ No newline at end of file From 4412976f34515399f2eabe4d50cef71657e4e238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionic=C4=83=20Biz=C4=83u?= Date: Tue, 30 Aug 2022 15:53:46 +0300 Subject: [PATCH 7/7] Updated docs --- .gitignore | 1 + DOCUMENTATION.md | 3 -- README.md | 75 +++++++++++++++++++++++------------------------ package-lock.json | 4 +-- 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/.gitignore b/.gitignore index 36c84a5..2ca2591 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ node_modules *.env .DS_Store +package-lock.json .bloggify/* diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index ff81b2b..3e670d1 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -5,9 +5,6 @@ You can see below the API reference of this module. ### `interopDefaultLegacy()` #__PURE__ -### `interopDefaultLegacy()` -#__PURE__ - ### `parseUrl(url, normalize)` Parses the input url. diff --git a/README.md b/README.md index 7e2de0f..911c8a3 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ # parse-url - [![Support me on Patreon][badge_patreon]][patreon] [![Buy me a book][badge_amazon]][amazon] [![PayPal][badge_paypal_donate]][paypal-donations] [![Ask me anything](https://img.shields.io/badge/ask%20me-anything-1abc9c.svg)](https://github.com/IonicaBizau/ama) [![Travis](https://img.shields.io/travis/IonicaBizau/parse-url.svg)](https://travis-ci.org/IonicaBizau/parse-url/) [![Version](https://img.shields.io/npm/v/parse-url.svg)](https://www.npmjs.com/package/parse-url) [![Downloads](https://img.shields.io/npm/dt/parse-url.svg)](https://www.npmjs.com/package/parse-url) [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/johnnyb?utm_source=github&utm_medium=button&utm_term=johnnyb&utm_campaign=github) + [![Support me on Patreon][badge_patreon]][patreon] [![Buy me a book][badge_amazon]][amazon] [![PayPal][badge_paypal_donate]][paypal-donations] [![Ask me anything](https://img.shields.io/badge/ask%20me-anything-1abc9c.svg)](https://github.com/IonicaBizau/ama) [![Version](https://img.shields.io/npm/v/parse-url.svg)](https://www.npmjs.com/package/parse-url) [![Downloads](https://img.shields.io/npm/dt/parse-url.svg)](https://www.npmjs.com/package/parse-url) [![Get help on Codementor](https://cdn.codementor.io/badges/get_help_github.svg)](https://www.codementor.io/johnnyb?utm_source=github&utm_medium=button&utm_term=johnnyb&utm_campaign=github) Buy Me A Coffee @@ -77,7 +77,7 @@ yarn add parse-url ```js // Dependencies -import parseUrl from "parse-url"; +import parseUrl from "../lib/index.js"; console.log(parseUrl("http://ionicabizau.net/blog")) // { @@ -168,9 +168,6 @@ There are few ways to get help: ## :memo: Documentation -### `interopDefaultLegacy()` -#__PURE__ - ### `interopDefaultLegacy()` #__PURE__ @@ -261,7 +258,6 @@ If you are using this library in one of your projects, add it in this list. :spa - `stun` - `@open-wa/wa-automate` - `kakapo` - - `@pushrocks/smarturl` - `parse-db-uri` - `fuge-runner` - `url-local` @@ -279,55 +275,55 @@ If you are using this library in one of your projects, add it in this list. :spa - `@hemith/react-native-tnk` - `@kriblet/wa-automate` - `@notnuzzel/crawl` - - `native-kakao-login` - `gitlab-backup-util-harduino` - `miguelcostero-ng2-toasty` + - `native-kakao-login` - `npm_one_1_2_3` - - `react-native-arunmeena1987` - `react-native-biometric-authenticate` + - `react-native-arunmeena1987` - `react-native-contact-list` - - `react-native-is7` - `react-native-payu-payment-testing` - - `react-native-kakao-maps` + - `react-native-is7` - `react-native-my-first-try-arun-ramya` + - `react-native-kakao-maps` - `react-native-ytximkit` - - `@positionex/position-sdk` + - `rn-adyen-dropin` - `begg` + - `@positionex/position-sdk` - `@corelmax/react-native-my2c2p-sdk` - `@felipesimmi/react-native-datalogic-module` - - `@jprustv/sulla-hotfix` - `@hawkingnetwork/react-native-tab-view` + - `@jprustv/sulla-hotfix` - `@mergulhao/wa-automate` - `cli-live-tutorial` - `drowl-base-theme-iconset` - `native-apple-login` - `react-native-cplus` - `npm_qwerty` - - `ssh-host-manager` - - `soajs.repositories` - - `react-native-arunjeyam1987` - `vrt-cli` - `vue-cli-plugin-ice-builder` - - `graphmilker` + - `react-native-arunjeyam1987` + - `soajs.repositories` + - `ssh-host-manager` - `native-zip` - - `react-native-flyy` + - `graphmilker` - `react-native-bubble-chart` - `verify-aws-sns-signature` - `@dataparty/api` + - `react-native-flyy` + - `@react-18-pdf/root` - `@apardellass/react-native-audio-stream` - `@geeky-apo/react-native-advanced-clipboard` - `@hsui/plugin-wss` - - `@saad27/react-native-bottom-tab-tour` - - `@roshub/api` - - `candlelabssdk` - `blitzzz` + - `candlelabssdk` + - `@roshub/api` + - `@saad27/react-native-bottom-tab-tour` - `generator-bootstrap-boilerplate-template` - - `react-native-dsphoto-module` - - `react-native-responsive-size` - - `react-native-sayhello-module` - `npm_one_12_34_1_` - `npm_one_2_2` - `payutesting` + - `react-native-responsive-size` - `vue-cli-plugin-ut-builder` - `xbuilder-forms` - `deploy-versioning` @@ -338,30 +334,30 @@ If you are using this library in one of your projects, add it in this list. :spa - `react-native-shekhar-bridge-test` - `loast` - `react-feedback-sdk` + - `@oiti/documentoscopy-react-native` + - `@snyk/sweater-comb` + - `@angga30prabu/wa-modified` + - `@hstech/utils` + - `birken-react-native-community-image-editor` + - `get-tarball-cli` + - `luojia-cli-dev` - `reac-native-arun-ramya-test` - - `react-native-arun-ramya-test` - - `react-native-arunramya151` - `react-native-plugpag-wrapper` - `react-native-pulsator-native` + - `react-native-arun-ramya-test` + - `react-native-arunramya151` - `react-native-transtracker-library` - `workpad` - - `@angga30prabu/wa-modified` - - `@hstech/utils` - - `get-tarball-cli` - - `luojia-cli-dev` - - `birken-react-native-community-image-editor` - `delta-screen` - `microbe.js` - - `@lakutata-module/service` - `ndla-source-map-resolver` - - `@screeb/react-native` - `@jfilipe-sparta/react-native-module_2` - - `@jimengio/mocked-proxy` + - `cogoportutils` + - `@lakutata-module/service` - `@buganto/client` - `@mockswitch/cli` - - `api-reach-react-native-fix` - `angularvezba` - - `astra-ufo-sdk` + - `api-reach-react-native-fix` - `react-native-syan-photo-picker` - `@wecraftapps/react-native-use-keyboard` - `hui-plugin-wss` @@ -370,18 +366,19 @@ If you are using this library in one of your projects, add it in this list. :spa - `raact-native-arunramya151` - `react-native-modal-progress-bar` - `react-native-test-module-hhh` - - `wander-cli` - - `react-native-badge-control` - `react-native-jsi-device-info` - - `normalize-ssh-url` + - `react-native-badge-control` + - `wander-cli` - `heroku-wp-environment-sync` - `hubot-will-it-connect` + - `normalize-ssh-url` - `ba-js-cookie-banner` - - `@ndla/source-map-resolver` - `ts-scraper` - `electron-info` - `rn-tm-notify` - `native-date-picker-module` + - `@ndla/source-map-resolver` + - `@jimengio/mocked-proxy` diff --git a/package-lock.json b/package-lock.json index 8d16f7a..f951712 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "parse-url", - "version": "8.0.0", + "version": "8.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "parse-url", - "version": "8.0.0", + "version": "8.1.0", "license": "MIT", "dependencies": { "parse-path": "^7.0.0"