diff --git a/lib/index.js b/lib/index.js index 16ffe4d..2da330b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,8 +1,4 @@ -// Dependencies const protocols = require("protocols") - , isSsh = require("is-ssh") - , qs = require("query-string") - ; /** * parsePath @@ -25,110 +21,50 @@ const protocols = require("protocols") * - `query` (Object): The url querystring, parsed as object. */ function parsePath(url) { - url = (url || "").trim().replace(/\r?\n|\r/gm, "") - var output = { - protocols: protocols(url) - , protocol: null - , port: null - , resource: "" - , user: "" - , pathname: "" - , hash: "" - , search: "" - , href: url - , query: Object.create(null) - } - , protocolIndex = url.indexOf("://") - , resourceIndex = -1 - , splits = null - , parts = null - ; - if (url.startsWith(".")) { - if (url.startsWith("./")) { - url = url.substring(2); - } - output.pathname = url; - output.protocol = "file"; + const output = { + protocols: [] + , protocol: null + , port: null + , resource: "" + , user: "" + , password: "" + , pathname: "" + , hash: "" + , search: "" + , href: url + , query: {} } - const firstChar = url.charAt(1) - if (!output.protocol) { + try { + const parsed = new URL(url) + output.protocols = protocols(parsed) output.protocol = output.protocols[0] - if (!output.protocol) { - if (isSsh(url)) { - output.protocol = "ssh" - } else if (firstChar === "/" || firstChar === "~") { - url = url.substring(2) - output.protocol = "file" - } else { - output.protocol = "file" - } - } - } - - if (protocolIndex !== -1) { - url = url.substring(protocolIndex + 3); - } - - parts = url.split(/\/|\\/); - if (output.protocol !== "file") { - output.resource = parts.shift(); - } else { - output.resource = ""; - } - - // user@domain - splits = output.resource.split("@"); - if (splits.length === 2) { - output.user = splits[0]; - output.resource = splits[1]; - } - - - // domain.com:port - splits = output.resource.split(":"); - if (splits.length === 2) { - output.resource = splits[0]; - const port = splits[1]; - if (port) { - output.port = Number(port); - if (isNaN(output.port) || port.match(/^\d+$/) === null) { - output.port = null; - parts.unshift(port); - } - } else { - output.port = null - } - } - - // Remove empty elements - parts = parts.filter(Boolean); - - // Stringify the pathname - if (output.protocol === "file") { - output.pathname = output.href - } else { - output.pathname = output.pathname || ((output.protocol !== "file" || output.href[0] === "/" ? "/" : "") + parts.join("/")); - } - - // #some-hash - splits = output.pathname.split("#"); - if (splits.length === 2) { - output.pathname = splits[0]; - output.hash = splits[1]; - } - - // ?foo=bar - splits = output.pathname.split("?"); - if (splits.length === 2) { - output.pathname = splits[0]; - output.search = splits[1]; + output.port = parsed.port + output.resource = parsed.host + output.user = parsed.username || "" + output.password = parsed.password || "" + output.pathname = parsed.pathname + output.hash = parsed.hash.slice(1) + output.search = parsed.search.slice(1) + output.href = parsed.href + output.query = Object.fromEntries(parsed.searchParams) + } catch (e) { + // TODO Maybe check if it is a valid local file path + // In any case, these will be parsed by higher + // level parsers such as parse-url, git-url-parse, git-up + output.protocols = ["file"] + output.protocol = output.protocols[0] + output.port = "" + output.resource = "" + output.user = "" + output.pathname = "" + output.hash = "" + output.search = "" + output.href = url + output.query = {} } - output.query = qs.parse(output.search); - output.href = output.href.replace(/\/$/, "") - output.pathname = output.pathname.replace(/\/$/, "") return output; } diff --git a/package.json b/package.json index 960f3b1..cff33ae 100644 --- a/package.json +++ b/package.json @@ -52,4 +52,4 @@ "bloggify.json", "bloggify/" ] -} \ No newline at end of file +} diff --git a/test/index.js b/test/index.js index 1f98d4d..daba258 100644 --- a/test/index.js +++ b/test/index.js @@ -1,7 +1,6 @@ // Dependencies const parseUrl = require("../lib") , tester = require("tester") - , qs = require("querystring") ; const INPUTS = [ @@ -10,12 +9,14 @@ const INPUTS = [ , { protocols: [ "http" ] , protocol: "http" - , port: null + , port: "" , resource: "ionicabizau.net" , user: "" , pathname: "/blog" , hash: "" , search: "" + , href: "http://ionicabizau.net/blog" + , query: {} } ] , [ @@ -23,12 +24,14 @@ const INPUTS = [ , { protocols: [ "http" ] , protocol: "http" - , port: null + , port: "" , resource: "ionicabizau.net" , user: "" , pathname: "/blog" , hash: "" , search: "" + , href: "http://ionicabizau.net/blog" + , query: {} } ] , [ @@ -36,12 +39,14 @@ const INPUTS = [ , { protocols: ["http"] , protocol: "http" - , port: null + , port: "" , resource: "domain.com" , user: "" , pathname: "/path/name" , hash: "some-hash" , search: "foo=bar&bar=42" + , query: { foo: "bar", bar: "42" } + , href: "http://domain.com/path/name?foo=bar&bar=42#some-hash" } ] , [ @@ -49,12 +54,14 @@ const INPUTS = [ , { protocols: ["http"] , protocol: "http" - , port: null + , port: "" , resource: "domain.com" , user: "" , pathname: "/path/name" , hash: "some-hash?foo=bar&bar=42" , search: "" + , query: {} + , href: "http://domain.com/path/name#some-hash?foo=bar&bar=42" } ] , [ @@ -62,137 +69,161 @@ const INPUTS = [ , { protocols: ["git", "ssh"] , protocol: "git" - , port: null + , port: "" , resource: "host.xz" , user: "git" , pathname: "/path/name.git" , hash: "" , search: "" + , query: {} + , href: "git+ssh://git@host.xz/path/name.git" } ] , [ + // NOTE:parse-path will look at this as a local path + // For parsing it as url, please use parse-url "git@github.com:IonicaBizau/git-stats.git" , { - protocols: [] - , protocol: "ssh" - , port: null - , resource: "github.com" - , user: "git" - , pathname: "/IonicaBizau/git-stats.git" + protocols: ["file"] + , protocol: "file" + , port: "" + , resource: "" + , user: "" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "git@github.com:IonicaBizau/git-stats.git" } ] , [ "/path/to/file.png" , { - protocols: [] + protocols: ["file"] , protocol: "file" - , port: null + , port: "" , resource: "" , user: "" - , pathname: "/path/to/file.png" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "/path/to/file.png" } ] , [ "./path/to/file.png" , { - protocols: [] + protocols: ["file"] , protocol: "file" - , port: null + , port: "" , resource: "" , user: "" - , pathname: "./path/to/file.png" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "./path/to/file.png" } ] , [ "./.path/to/file.png" , { - protocols: [] + protocols: ["file"] , protocol: "file" - , port: null + , port: "" , resource: "" , user: "" - , pathname: "./.path/to/file.png" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "./.path/to/file.png" } ] , [ ".path/to/file.png" , { - protocols: [] + protocols: ["file"] , protocol: "file" - , port: null + , port: "" , resource: "" , user: "" - , pathname: ".path/to/file.png" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: ".path/to/file.png" } ] , [ "path/to/file.png" , { - protocols: [] + protocols: ["file"] , protocol: "file" - , port: null + , port: "" , resource: "" , user: "" - , pathname: "path/to/file.png" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "path/to/file.png" } ], [ "git@github.com:9IonicaBizau/git-stats.git" , { - protocols: [] - , protocol: "ssh" - , port: null - , resource: "github.com" - , user: "git" - , pathname: "/9IonicaBizau/git-stats.git" + protocols: ["file"] + , protocol: "file" + , port: "" + , resource: "" + , user: "" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "git@github.com:9IonicaBizau/git-stats.git" } ], [ "git@github.com:0xABC/git-stats.git" , { - protocols: [] - , protocol: "ssh" - , port: null - , resource: "github.com" - , user: "git" - , pathname: "/0xABC/git-stats.git" + protocols: ["file"] + , protocol: "file" + , port: "" + , resource: "" + , user: "" + , pathname: "" , hash: "" , search: "" + , query: {} + , href: "git@github.com:0xABC/git-stats.git" } ], [ "https://attacker.com\\@example.com" , { protocols: ["https"] , protocol: "https" - , port: null + , port: "" , resource: "attacker.com" , user: "" , pathname: "/@example.com" , hash: "" , search: "" + , href: "https://attacker.com/@example.com" + , query: {} } ], [ "jav\r\nascript://%0aalert(1)" , { protocols: ["javascript"] , protocol: "javascript" - , port: null + , port: "" , resource: "%0aalert(1)" , user: "" , pathname: "" , hash: "" + , href: "javascript://%0aalert(1)" + , query: {} , search: "" } ] @@ -201,9 +232,9 @@ const INPUTS = [ tester.describe("check urls", test => { INPUTS.forEach(function (c) { test.should("support " + c[0], () => { - c[1].href = c[0].trim().replace(/\r?\n|\r/gm, ""); - c[1].query = qs.parse(c[1].search) - test.expect(parseUrl(c[0])).toEqual(c[1]); + const cParsed = parseUrl(c[0]) + debugger + test.expect(cParsed).toEqual(c[1]); }); }); });