diff --git a/lib/source-map-consumer.js b/lib/source-map-consumer.js
index 397b2b10..9b68e393 100644
--- a/lib/source-map-consumer.js
+++ b/lib/source-map-consumer.js
@@ -194,6 +194,8 @@ class BasicSourceMapConsumer extends SourceMapConsumer {
throw new Error("Unsupported version: " + version);
}
+ that._sourceLookupCache = new Map();
+
// Pass `true` below to allow duplicate names and sources. While source maps
// are intended to be compressed and deduplicated, the TypeScript compiler
// sometimes generates source maps with duplicates in them. See Github issue
@@ -227,18 +229,30 @@ class BasicSourceMapConsumer extends SourceMapConsumer {
* found.
*/
_findSourceIndex(aSource) {
+ // In the most common usecases, we'll be constantly looking up the index for the same source
+ // files, so we cache the index lookup to avoid constantly recomputing the full URLs.
+ const cachedIndex = this._sourceLookupCache.get(aSource);
+ if (typeof cachedIndex === "number") {
+ return cachedIndex;
+ }
+
// Treat the source as map-relative overall by default.
const sourceAsMapRelative = util.computeSourceURL(null, aSource, this._sourceMapURL);
if (this._absoluteSources.has(sourceAsMapRelative)) {
- return this._absoluteSources.indexOf(sourceAsMapRelative);
+ const index = this._absoluteSources.indexOf(sourceAsMapRelative);
+ this._sourceLookupCache.set(aSource, index);
+ return index;
}
// Fall back to treating the source as sourceRoot-relative.
const sourceAsSourceRootRelative = util.computeSourceURL(this.sourceRoot, aSource, this._sourceMapURL);
if (this._absoluteSources.has(sourceAsSourceRootRelative)) {
- return this._absoluteSources.indexOf(sourceAsSourceRootRelative);
+ const index = this._absoluteSources.indexOf(sourceAsSourceRootRelative);
+ this._sourceLookupCache.set(aSource, index);
+ return index;
}
+ // To avoid this cache growing forever, we do not cache lookup misses.
return -1;
}
diff --git a/lib/url-browser.js b/lib/url-browser.js
new file mode 100644
index 00000000..c9da02a8
--- /dev/null
+++ b/lib/url-browser.js
@@ -0,0 +1,21 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+"use strict";
+
+/**
+ * Browser 'URL' implementations have been found to handle non-standard URL
+ * schemes poorly, and schemes like
+ *
+ * webpack:///src/folder/file.js
+ *
+ * are very common in source maps. For the time being we use a JS
+ * implementation in these contexts instead. See
+ *
+ * * https://bugzilla.mozilla.org/show_bug.cgi?id=1374505
+ * * https://bugs.chromium.org/p/chromium/issues/detail?id=734880
+ */
+module.exports = require("whatwg-url").URL;
diff --git a/lib/url.js b/lib/url.js
new file mode 100644
index 00000000..8bb71b6a
--- /dev/null
+++ b/lib/url.js
@@ -0,0 +1,13 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+"use strict";
+
+// Note: This file is overridden in the 'package.json#browser' field to
+// substitute lib/url-browser.js instead.
+
+// Use the URL global for Node 10, and the 'url' module for Node 8.
+module.exports = typeof URL === "function" ? URL : require("url").URL;
diff --git a/lib/util.js b/lib/util.js
index ae0897cd..430ec4de 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -5,6 +5,8 @@
* http://opensource.org/licenses/BSD-3-Clause
*/
+const URL = require("./url");
+
/**
* This is a helper function for getting values from parameter/options
* objects.
@@ -26,261 +28,6 @@ function getArg(aArgs, aName, aDefaultValue) {
}
exports.getArg = getArg;
-const urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;
-const dataUrlRegexp = /^data:.+\,.+$/;
-
-function urlParse(aUrl) {
- const match = aUrl.match(urlRegexp);
- if (!match) {
- return null;
- }
- return {
- scheme: match[1],
- auth: match[2],
- host: match[3],
- port: match[4],
- path: match[5]
- };
-}
-exports.urlParse = urlParse;
-
-function urlGenerate(aParsedUrl) {
- let url = "";
- if (aParsedUrl.scheme) {
- url += aParsedUrl.scheme + ":";
- }
- url += "//";
- if (aParsedUrl.auth) {
- url += aParsedUrl.auth + "@";
- }
- if (aParsedUrl.host) {
- url += aParsedUrl.host;
- }
- if (aParsedUrl.port) {
- url += ":" + aParsedUrl.port;
- }
- if (aParsedUrl.path) {
- url += aParsedUrl.path;
- }
- return url;
-}
-exports.urlGenerate = urlGenerate;
-
-const MAX_CACHED_INPUTS = 32;
-
-/**
- * Takes some function `f(input) -> result` and returns a memoized version of
- * `f`.
- *
- * We keep at most `MAX_CACHED_INPUTS` memoized results of `f` alive. The
- * memoization is a dumb-simple, linear least-recently-used cache.
- */
-function lruMemoize(f) {
- const cache = [];
-
- return function(input) {
- for (let i = 0; i < cache.length; i++) {
- if (cache[i].input === input) {
- const temp = cache[0];
- cache[0] = cache[i];
- cache[i] = temp;
- return cache[0].result;
- }
- }
-
- const result = f(input);
-
- cache.unshift({
- input,
- result,
- });
-
- if (cache.length > MAX_CACHED_INPUTS) {
- cache.pop();
- }
-
- return result;
- };
-}
-
-/**
- * Normalizes a path, or the path portion of a URL:
- *
- * - Replaces consecutive slashes with one slash.
- * - Removes unnecessary '.' parts.
- * - Removes unnecessary '
/..' parts.
- *
- * Based on code in the Node.js 'path' core module.
- *
- * @param aPath The path or url to normalize.
- */
-const normalize = lruMemoize(function normalize(aPath) {
- let path = aPath;
- const url = urlParse(aPath);
- if (url) {
- if (!url.path) {
- return aPath;
- }
- path = url.path;
- }
- const isAbsolute = exports.isAbsolute(path);
-
- // Split the path into parts between `/` characters. This is much faster than
- // using `.split(/\/+/g)`.
- const parts = [];
- let start = 0;
- let i = 0;
- while (true) {
- start = i;
- i = path.indexOf("/", start);
- if (i === -1) {
- parts.push(path.slice(start));
- break;
- } else {
- parts.push(path.slice(start, i));
- while (i < path.length && path[i] === "/") {
- i++;
- }
- }
- }
-
- let up = 0;
- for (i = parts.length - 1; i >= 0; i--) {
- const part = parts[i];
- if (part === ".") {
- parts.splice(i, 1);
- } else if (part === "..") {
- up++;
- } else if (up > 0) {
- if (part === "") {
- // The first part is blank if the path is absolute. Trying to go
- // above the root is a no-op. Therefore we can remove all '..' parts
- // directly after the root.
- parts.splice(i + 1, up);
- up = 0;
- } else {
- parts.splice(i, 2);
- up--;
- }
- }
- }
- path = parts.join("/");
-
- if (path === "") {
- path = isAbsolute ? "/" : ".";
- }
-
- if (url) {
- url.path = path;
- return urlGenerate(url);
- }
- return path;
-});
-exports.normalize = normalize;
-
-/**
- * Joins two paths/URLs.
- *
- * @param aRoot The root path or URL.
- * @param aPath The path or URL to be joined with the root.
- *
- * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a
- * scheme-relative URL: Then the scheme of aRoot, if any, is prepended
- * first.
- * - Otherwise aPath is a path. If aRoot is a URL, then its path portion
- * is updated with the result and aRoot is returned. Otherwise the result
- * is returned.
- * - If aPath is absolute, the result is aPath.
- * - Otherwise the two paths are joined with a slash.
- * - Joining for example 'http://' and 'www.example.com' is also supported.
- */
-function join(aRoot, aPath) {
- if (aRoot === "") {
- aRoot = ".";
- }
- if (aPath === "") {
- aPath = ".";
- }
- const aPathUrl = urlParse(aPath);
- const aRootUrl = urlParse(aRoot);
- if (aRootUrl) {
- aRoot = aRootUrl.path || "/";
- }
-
- // `join(foo, '//www.example.org')`
- if (aPathUrl && !aPathUrl.scheme) {
- if (aRootUrl) {
- aPathUrl.scheme = aRootUrl.scheme;
- }
- return urlGenerate(aPathUrl);
- }
-
- if (aPathUrl || aPath.match(dataUrlRegexp)) {
- return aPath;
- }
-
- // `join('http://', 'www.example.com')`
- if (aRootUrl && !aRootUrl.host && !aRootUrl.path) {
- aRootUrl.host = aPath;
- return urlGenerate(aRootUrl);
- }
-
- const joined = aPath.charAt(0) === "/"
- ? aPath
- : normalize(aRoot.replace(/\/+$/, "") + "/" + aPath);
-
- if (aRootUrl) {
- aRootUrl.path = joined;
- return urlGenerate(aRootUrl);
- }
- return joined;
-}
-exports.join = join;
-
-exports.isAbsolute = function(aPath) {
- return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
-};
-
-/**
- * Make a path relative to a URL or another path.
- *
- * @param aRoot The root path or URL.
- * @param aPath The path or URL to be made relative to aRoot.
- */
-function relative(aRoot, aPath) {
- if (aRoot === "") {
- aRoot = ".";
- }
-
- aRoot = aRoot.replace(/\/$/, "");
-
- // It is possible for the path to be above the root. In this case, simply
- // checking whether the root is a prefix of the path won't work. Instead, we
- // need to remove components from the root one by one, until either we find
- // a prefix that fits, or we run out of components to remove.
- let level = 0;
- while (aPath.indexOf(aRoot + "/") !== 0) {
- const index = aRoot.lastIndexOf("/");
- if (index < 0) {
- return aPath;
- }
-
- // If the only part of the root that is left is the scheme (i.e. http://,
- // file:///, etc.), one or more slashes (/), or simply nothing at all, we
- // have exhausted all components, so the path is not relative to the root.
- aRoot = aRoot.slice(0, index);
- if (aRoot.match(/^([^\/]+:\/)?\/*$/)) {
- return aPath;
- }
-
- ++level;
- }
-
- // Make sure we add a "../" for each component we removed from the root.
- return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
-}
-exports.relative = relative;
-
const supportsNullProto = (function() {
const obj = Object.create(null);
return !("__proto__" in obj);
@@ -415,55 +162,277 @@ function parseSourceMapInput(str) {
}
exports.parseSourceMapInput = parseSourceMapInput;
+// We use 'http' as the base here because we want URLs processed relative
+// to the safe base to be treated as "special" URLs during parsing using
+// the WHATWG URL parsing. This ensures that backslash normalization
+// applies to the path and such.
+const PROTOCOL = "http:";
+const PROTOCOL_AND_HOST = `${PROTOCOL}//host`;
+
/**
- * Compute the URL of a source given the the source root, the source's
- * URL, and the source map's URL.
+ * Make it easy to create small utilities that tweak a URL's path.
*/
-function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
- sourceURL = sourceURL || "";
-
- if (sourceRoot) {
- // This follows what Chrome does.
- if (sourceRoot[sourceRoot.length - 1] !== "/" && sourceURL[0] !== "/") {
- sourceRoot += "/";
+function createSafeHandler(cb) {
+ return input => {
+ const type = getURLType(input);
+ const base = buildSafeBase(input);
+ const url = new URL(input, base);
+
+ cb(url);
+
+ const result = url.toString();
+
+ if (type === "absolute") {
+ return result;
+ } else if (type === "scheme-relative") {
+ return result.slice(PROTOCOL.length);
+ } else if (type === "path-absolute") {
+ return result.slice(PROTOCOL_AND_HOST.length);
}
- // The spec says:
- // Line 4: An optional source root, useful for relocating source
- // files on a server or removing repeated values in the
- // “sources” entry. This value is prepended to the individual
- // entries in the “source” field.
- sourceURL = sourceRoot + sourceURL;
+
+ // This assumes that the callback will only change
+ // the path, search and hash values.
+ return computeRelativeURL(base, result);
+ };
+}
+
+function withBase(url, base) {
+ return new URL(url, base).toString();
+}
+
+function buildUniqueSegment(prefix, str) {
+ let id = 0;
+ do {
+ const ident = prefix + (id++);
+ if (str.indexOf(ident) === -1) return ident;
+ } while (true);
+}
+
+function buildSafeBase(str) {
+ const maxDotParts = str.split("..").length - 1;
+
+ // If we used a segment that also existed in `str`, then we would be unable
+ // to compute relative paths. For example, if `segment` were just "a":
+ //
+ // const url = "../../a/"
+ // const base = buildSafeBase(url); // http://host/a/a/
+ // const joined = "http://host/a/";
+ // const result = relative(base, joined);
+ //
+ // Expected: "../../a/";
+ // Actual: "a/"
+ //
+ const segment = buildUniqueSegment("p", str);
+
+ let base = `${PROTOCOL_AND_HOST}/`;
+ for (let i = 0; i < maxDotParts; i++) {
+ base += `${segment}/`;
+ }
+ return base;
+}
+
+const ABSOLUTE_SCHEME = /^[A-Za-z0-9\+\-\.]+:/;
+function getURLType(url) {
+ if (url[0] === "/") {
+ if (url[1] === "/") return "scheme-relative";
+ return "path-absolute";
+ }
+
+ return ABSOLUTE_SCHEME.test(url) ? "absolute" : "path-relative";
+}
+
+/**
+ * Given two URLs that are assumed to be on the same
+ * protocol/host/user/password build a relative URL from the
+ * path, params, and hash values.
+ *
+ * @param rootURL The root URL that the target will be relative to.
+ * @param targetURL The target that the relative URL points to.
+ * @return A rootURL-relative, normalized URL value.
+ */
+function computeRelativeURL(rootURL, targetURL) {
+ if (typeof rootURL === "string") rootURL = new URL(rootURL);
+ if (typeof targetURL === "string") targetURL = new URL(targetURL);
+
+ const targetParts = targetURL.pathname.split("/");
+ const rootParts = rootURL.pathname.split("/");
+
+ // If we've got a URL path ending with a "/", we remove it since we'd
+ // otherwise be relative to the wrong location.
+ if (rootParts.length > 0 && !rootParts[rootParts.length - 1]) {
+ rootParts.pop();
+ }
+
+ while (
+ targetParts.length > 0 &&
+ rootParts.length > 0 &&
+ targetParts[0] === rootParts[0]
+ ) {
+ targetParts.shift();
+ rootParts.shift();
+ }
+
+ const relativePath = rootParts
+ .map(() => "..")
+ .concat(targetParts)
+ .join("/");
+
+ return relativePath + targetURL.search + targetURL.hash;
+}
+
+/**
+ * Given a URL, ensure that it is treated as a directory URL.
+ *
+ * @param url
+ * @return A normalized URL value.
+ */
+const ensureDirectory = createSafeHandler(url => {
+ url.pathname = url.pathname.replace(/\/?$/, "/");
+});
+
+/**
+ * Given a URL, strip off any filename if one is present.
+ *
+ * @param url
+ * @return A normalized URL value.
+ */
+const trimFilename = createSafeHandler(url => {
+ url.href = new URL(".", url.toString()).toString();
+});
+
+/**
+ * Normalize a given URL.
+ * * Convert backslashes.
+ * * Remove any ".." and "." segments.
+ *
+ * @param url
+ * @return A normalized URL value.
+ */
+const normalize = createSafeHandler(url => {});
+exports.normalize = normalize;
+
+/**
+ * Joins two paths/URLs.
+ *
+ * All returned URLs will be normalized.
+ *
+ * @param aRoot The root path or URL. Assumed to reference a directory.
+ * @param aPath The path or URL to be joined with the root.
+ * @return A joined and normalized URL value.
+ */
+function join(aRoot, aPath) {
+ const pathType = getURLType(aPath);
+ const rootType = getURLType(aRoot);
+
+ aRoot = ensureDirectory(aRoot);
+
+ if (pathType === "absolute") {
+ return withBase(aPath, undefined);
+ }
+ if (rootType === "absolute") {
+ return withBase(aPath, aRoot);
+ }
+
+ if (pathType === "scheme-relative") {
+ return normalize(aPath);
+ }
+ if (rootType === "scheme-relative") {
+ return withBase(aPath, withBase(aRoot, PROTOCOL_AND_HOST)).slice(PROTOCOL.length);
+ }
+
+ if (pathType === "path-absolute") {
+ return normalize(aPath);
+ }
+ if (rootType === "path-absolute") {
+ return withBase(aPath, withBase(aRoot, PROTOCOL_AND_HOST)).slice(PROTOCOL_AND_HOST.length);
+ }
+
+ const base = buildSafeBase(aPath + aRoot);
+ const newPath = withBase(aPath, withBase(aRoot, base));
+ return computeRelativeURL(base, newPath);
+}
+exports.join = join;
+
+/**
+ * Make a path relative to a URL or another path. If returning a
+ * relative URL is not possible, the original target will be returned.
+ * All returned URLs will be normalized.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be made relative to aRoot.
+ * @return A rootURL-relative (if possible), normalized URL value.
+ */
+function relative(rootURL, targetURL) {
+ const result = relativeIfPossible(rootURL, targetURL);
+
+ return typeof result === "string" ? result : normalize(targetURL);
+}
+exports.relative = relative;
+
+function relativeIfPossible(rootURL, targetURL) {
+ const urlType = getURLType(rootURL);
+ if (urlType !== getURLType(targetURL)) {
+ return null;
+ }
+
+ const base = buildSafeBase(rootURL + targetURL);
+ const root = new URL(rootURL, base);
+ const target = new URL(targetURL, base);
+
+ try {
+ new URL("", target.toString());
+ } catch (err) {
+ // Bail if the URL doesn't support things being relative to it,
+ // For example, data: and blob: URLs.
+ return null;
}
- // Historically, SourceMapConsumer did not take the sourceMapURL as
- // a parameter. This mode is still somewhat supported, which is why
- // this code block is conditional. However, it's preferable to pass
- // the source map URL to SourceMapConsumer, so that this function
- // can implement the source URL resolution algorithm as outlined in
- // the spec. This block is basically the equivalent of:
- // new URL(sourceURL, sourceMapURL).toString()
- // ... except it avoids using URL, which wasn't available in the
- // older releases of node still supported by this library.
+ if (
+ target.protocol !== root.protocol ||
+ target.user !== root.user ||
+ target.password !== root.password ||
+ target.hostname !== root.hostname ||
+ target.port !== root.port
+ ) {
+ return null;
+ }
+
+ return computeRelativeURL(root, target);
+}
+
+/**
+ * Compute the URL of a source given the the source root, the source's
+ * URL, and the source map's URL.
+ */
+function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+ // The source map spec states that "sourceRoot" and "sources" entries are to be appended. While
+ // that is a little vague, implementations have generally interpreted that as joining the
+ // URLs with a `/` between then, assuming the "sourceRoot" doesn't already end with one.
+ // For example,
//
- // The spec says:
- // If the sources are not absolute URLs after prepending of the
- // “sourceRoot”, the sources are resolved relative to the
- // SourceMap (like resolving script src in a html document).
- if (sourceMapURL) {
- const parsed = urlParse(sourceMapURL);
- if (!parsed) {
- throw new Error("sourceMapURL could not be parsed");
- }
- if (parsed.path) {
- // Strip the last path component, but keep the "/".
- const index = parsed.path.lastIndexOf("/");
- if (index >= 0) {
- parsed.path = parsed.path.substring(0, index + 1);
- }
- }
- sourceURL = join(urlGenerate(parsed), sourceURL);
+ // sourceRoot: "some-dir",
+ // sources: ["/some-path.js"]
+ //
+ // and
+ //
+ // sourceRoot: "some-dir/",
+ // sources: ["/some-path.js"]
+ //
+ // must behave as "some-dir/some-path.js".
+ //
+ // With this library's the transition to a more URL-focused implementation, that behavior is
+ // preserved here. To acheive that, we trim the "/" from absolute-path when a sourceRoot value
+ // is present in order to make the sources entries behave as if they are relative to the
+ // "sourceRoot", as they would have if the two strings were simply concated.
+ if (sourceRoot && getURLType(sourceURL) === "path-absolute") {
+ sourceURL = sourceURL.replace(/^\//, "");
}
- return normalize(sourceURL);
+ let url = normalize(sourceURL || "");
+
+ // Parsing URLs can be expensive, so we only perform these joins when needed.
+ if (sourceRoot) url = join(sourceRoot, url);
+ if (sourceMapURL) url = join(trimFilename(sourceMapURL), url);
+ return url;
}
exports.computeSourceURL = computeSourceURL;
diff --git a/package.json b/package.json
index ec69a25f..9bffc2b7 100644
--- a/package.json
+++ b/package.json
@@ -49,6 +49,7 @@
"main": "./source-map.js",
"types": "./source-map.d.ts",
"browser": {
+ "./lib/url.js": "./lib/url-browser.js",
"./lib/read-wasm.js": "./lib/read-wasm-browser.js"
},
"files": [
@@ -84,5 +85,8 @@
"nyc": {
"reporter": "html"
},
- "typings": "source-map"
+ "typings": "source-map",
+ "dependencies": {
+ "whatwg-url": "^7.0.0"
+ }
}
diff --git a/test/test-source-map-consumer.js b/test/test-source-map-consumer.js
index d2d51a09..3d86b39e 100644
--- a/test/test-source-map-consumer.js
+++ b/test/test-source-map-consumer.js
@@ -1049,26 +1049,6 @@ exports["test sourceRoot + originalPositionFor"] = async function(assert) {
map.destroy();
};
-exports["test github issue #56"] = async function(assert) {
- let map = new SourceMapGenerator({
- sourceRoot: "http://",
- file: "www.example.com/foo.js"
- });
- map.addMapping({
- original: { line: 1, column: 1 },
- generated: { line: 2, column: 2 },
- source: "www.example.com/original.js"
- });
-
- map = await new SourceMapConsumer(map.toString());
-
- const sources = map.sources;
- assert.equal(sources.length, 1);
- assert.equal(sources[0], "http://www.example.com/original.js");
-
- map.destroy();
-};
-
// Was github issue #43, but that's no longer valid.
exports["test source resolution with sourceMapURL"] = async function(assert) {
let map = new SourceMapGenerator({
@@ -1444,7 +1424,7 @@ exports["test webpack URL resolution"] = async function(assert) {
const consumer = await new SourceMapConsumer(map);
assert.equal(consumer.sources.length, 1);
- assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap 67e184f9679733298d44");
+ assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap%2067e184f9679733298d44");
consumer.destroy();
};
@@ -1461,7 +1441,7 @@ exports["test webpack URL resolution with sourceMapURL"] = async function(assert
const consumer = await new SourceMapConsumer(map, "http://www.example.com/q.js.map");
assert.equal(consumer.sources.length, 1);
- assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap 67e184f9679733298d44");
+ assert.equal(consumer.sources[0], "webpack:///webpack/bootstrap%2067e184f9679733298d44");
consumer.destroy();
};
diff --git a/test/test-util.js b/test/test-util.js
index e665847c..1e335989 100644
--- a/test/test-util.js
+++ b/test/test-util.js
@@ -6,46 +6,6 @@
*/
const libUtil = require("../lib/util");
-
-exports["test urls"] = function(assert) {
- const assertUrl = function(url) {
- assert.equal(url, libUtil.urlGenerate(libUtil.urlParse(url)));
- };
- assertUrl("http://");
- assertUrl("http://www.example.com");
- assertUrl("http://user:pass@www.example.com");
- assertUrl("http://www.example.com:80");
- assertUrl("http://www.example.com/");
- assertUrl("http://www.example.com/foo/bar");
- assertUrl("http://www.example.com/foo/bar/");
- assertUrl("http://user:pass@www.example.com:80/foo/bar/");
-
- assertUrl("//");
- assertUrl("//www.example.com");
- assertUrl("file:///www.example.com");
-
- assert.equal(libUtil.urlParse(""), null);
- assert.equal(libUtil.urlParse("."), null);
- assert.equal(libUtil.urlParse(".."), null);
- assert.equal(libUtil.urlParse("a"), null);
- assert.equal(libUtil.urlParse("a/b"), null);
- assert.equal(libUtil.urlParse("a//b"), null);
- assert.equal(libUtil.urlParse("/a"), null);
- assert.equal(libUtil.urlParse("data:foo,bar"), null);
-
- let parsed = libUtil.urlParse("http://x-y.com/bar");
- assert.equal(parsed.scheme, "http");
- assert.equal(parsed.host, "x-y.com");
- assert.equal(parsed.path, "/bar");
-
- const webpackURL = "webpack:///webpack/bootstrap 67e184f9679733298d44";
- parsed = libUtil.urlParse(webpackURL);
- assert.equal(parsed.scheme, "webpack");
- assert.equal(parsed.host, "");
- assert.equal(parsed.path, "/webpack/bootstrap 67e184f9679733298d44");
- assert.equal(webpackURL, libUtil.urlGenerate(parsed));
-};
-
exports["test normalize()"] = function(assert) {
assert.equal(libUtil.normalize("/.."), "/");
assert.equal(libUtil.normalize("/../"), "/");
@@ -53,11 +13,12 @@ exports["test normalize()"] = function(assert) {
assert.equal(libUtil.normalize("/../../../../a/b/c"), "/a/b/c");
assert.equal(libUtil.normalize("/a/b/c/../../../d/../../e"), "/e");
- assert.equal(libUtil.normalize(".."), "..");
+ assert.equal(libUtil.normalize(".."), "../");
assert.equal(libUtil.normalize("../"), "../");
+
assert.equal(libUtil.normalize("../../a/"), "../../a/");
- assert.equal(libUtil.normalize("a/.."), ".");
- assert.equal(libUtil.normalize("a/../../.."), "../..");
+ assert.equal(libUtil.normalize("a/.."), "");
+ assert.equal(libUtil.normalize("a/../../.."), "../../");
assert.equal(libUtil.normalize("/."), "/");
assert.equal(libUtil.normalize("/./"), "/");
@@ -65,42 +26,43 @@ exports["test normalize()"] = function(assert) {
assert.equal(libUtil.normalize("/././././a/b/c"), "/a/b/c");
assert.equal(libUtil.normalize("/a/b/c/./././d/././e"), "/a/b/c/d/e");
- assert.equal(libUtil.normalize(""), ".");
- assert.equal(libUtil.normalize("."), ".");
- assert.equal(libUtil.normalize("./"), ".");
+ assert.equal(libUtil.normalize(""), "");
+ assert.equal(libUtil.normalize("."), "");
+ assert.equal(libUtil.normalize("./"), "");
assert.equal(libUtil.normalize("././a"), "a");
assert.equal(libUtil.normalize("a/./"), "a/");
- assert.equal(libUtil.normalize("a/././."), "a");
+ assert.equal(libUtil.normalize("a/././."), "a/");
- assert.equal(libUtil.normalize("/a/b//c////d/////"), "/a/b/c/d/");
- assert.equal(libUtil.normalize("///a/b//c////d/////"), "///a/b/c/d/");
- assert.equal(libUtil.normalize("a/b//c////d"), "a/b/c/d");
+ assert.equal(libUtil.normalize("/a/b//c////d/////"), "/a/b//c////d/////");
- assert.equal(libUtil.normalize(".///.././../a/b//./.."), "../../a");
+ assert.equal(libUtil.normalize("///a/b//c////d/////"), "//a/b//c////d/////");
+ assert.equal(libUtil.normalize("a/b//c////d"), "a/b//c////d");
- assert.equal(libUtil.normalize("http://www.example.com"), "http://www.example.com");
+ assert.equal(libUtil.normalize(".///.././../a/b//./.."), "a/b/");
+
+ assert.equal(libUtil.normalize("http://www.example.com"), "http://www.example.com/");
assert.equal(libUtil.normalize("http://www.example.com/"), "http://www.example.com/");
- assert.equal(libUtil.normalize("http://www.example.com/./..//a/b/c/.././d//"), "http://www.example.com/a/b/d/");
+ assert.equal(libUtil.normalize("http://www.example.com/./..//a/b/c/.././d//"), "http://www.example.com//a/b/d//");
};
exports["test join()"] = function(assert) {
assert.equal(libUtil.join("a", "b"), "a/b");
assert.equal(libUtil.join("a/", "b"), "a/b");
- assert.equal(libUtil.join("a//", "b"), "a/b");
+ assert.equal(libUtil.join("a//", "b"), "a//b");
assert.equal(libUtil.join("a", "b/"), "a/b/");
- assert.equal(libUtil.join("a", "b//"), "a/b/");
+ assert.equal(libUtil.join("a", "b//"), "a/b//");
assert.equal(libUtil.join("a/", "/b"), "/b");
- assert.equal(libUtil.join("a//", "//b"), "//b");
+ assert.equal(libUtil.join("a//", "//b"), "//b/");
- assert.equal(libUtil.join("a", ".."), ".");
+ assert.equal(libUtil.join("a", ".."), "");
assert.equal(libUtil.join("a", "../b"), "b");
assert.equal(libUtil.join("a/b", "../c"), "a/c");
- assert.equal(libUtil.join("a", "."), "a");
+ assert.equal(libUtil.join("a", "."), "a/");
assert.equal(libUtil.join("a", "./b"), "a/b");
assert.equal(libUtil.join("a/b", "./c"), "a/b/c");
- assert.equal(libUtil.join("a", "http://www.example.com"), "http://www.example.com");
+ assert.equal(libUtil.join("a", "http://www.example.com"), "http://www.example.com/");
assert.equal(libUtil.join("a", "data:foo,bar"), "data:foo,bar");
@@ -108,101 +70,96 @@ exports["test join()"] = function(assert) {
assert.equal(libUtil.join(".", "b"), "b");
assert.equal(libUtil.join("", "b/"), "b/");
assert.equal(libUtil.join(".", "b/"), "b/");
- assert.equal(libUtil.join("", "b//"), "b/");
- assert.equal(libUtil.join(".", "b//"), "b/");
+ assert.equal(libUtil.join("", "b//"), "b//");
+ assert.equal(libUtil.join(".", "b//"), "b//");
- assert.equal(libUtil.join("", ".."), "..");
- assert.equal(libUtil.join(".", ".."), "..");
+ assert.equal(libUtil.join("", ".."), "../");
+ assert.equal(libUtil.join(".", ".."), "../");
assert.equal(libUtil.join("", "../b"), "../b");
assert.equal(libUtil.join(".", "../b"), "../b");
- assert.equal(libUtil.join("", "."), ".");
- assert.equal(libUtil.join(".", "."), ".");
+ assert.equal(libUtil.join("", "."), "");
+ assert.equal(libUtil.join(".", "."), "");
assert.equal(libUtil.join("", "./b"), "b");
assert.equal(libUtil.join(".", "./b"), "b");
- assert.equal(libUtil.join("", "http://www.example.com"), "http://www.example.com");
- assert.equal(libUtil.join(".", "http://www.example.com"), "http://www.example.com");
+ assert.equal(libUtil.join("", "http://www.example.com"), "http://www.example.com/");
+ assert.equal(libUtil.join(".", "http://www.example.com"), "http://www.example.com/");
assert.equal(libUtil.join("", "data:foo,bar"), "data:foo,bar");
assert.equal(libUtil.join(".", "data:foo,bar"), "data:foo,bar");
assert.equal(libUtil.join("..", "b"), "../b");
assert.equal(libUtil.join("..", "b/"), "../b/");
- assert.equal(libUtil.join("..", "b//"), "../b/");
+ assert.equal(libUtil.join("..", "b//"), "../b//");
- assert.equal(libUtil.join("..", ".."), "../..");
+ assert.equal(libUtil.join("..", ".."), "../../");
assert.equal(libUtil.join("..", "../b"), "../../b");
- assert.equal(libUtil.join("..", "."), "..");
+ assert.equal(libUtil.join("..", "."), "../");
assert.equal(libUtil.join("..", "./b"), "../b");
- assert.equal(libUtil.join("..", "http://www.example.com"), "http://www.example.com");
+ assert.equal(libUtil.join("..", "http://www.example.com"), "http://www.example.com/");
assert.equal(libUtil.join("..", "data:foo,bar"), "data:foo,bar");
- assert.equal(libUtil.join("a", ""), "a");
- assert.equal(libUtil.join("a", "."), "a");
- assert.equal(libUtil.join("a/", ""), "a");
- assert.equal(libUtil.join("a/", "."), "a");
- assert.equal(libUtil.join("a//", ""), "a");
- assert.equal(libUtil.join("a//", "."), "a");
- assert.equal(libUtil.join("/a", ""), "/a");
- assert.equal(libUtil.join("/a", "."), "/a");
- assert.equal(libUtil.join("", ""), ".");
- assert.equal(libUtil.join(".", ""), ".");
- assert.equal(libUtil.join(".", ""), ".");
- assert.equal(libUtil.join(".", "."), ".");
- assert.equal(libUtil.join("..", ""), "..");
- assert.equal(libUtil.join("..", "."), "..");
- assert.equal(libUtil.join("http://foo.org/a", ""), "http://foo.org/a");
- assert.equal(libUtil.join("http://foo.org/a", "."), "http://foo.org/a");
- assert.equal(libUtil.join("http://foo.org/a/", ""), "http://foo.org/a");
- assert.equal(libUtil.join("http://foo.org/a/", "."), "http://foo.org/a");
- assert.equal(libUtil.join("http://foo.org/a//", ""), "http://foo.org/a");
- assert.equal(libUtil.join("http://foo.org/a//", "."), "http://foo.org/a");
+ assert.equal(libUtil.join("a", ""), "a/");
+ assert.equal(libUtil.join("a", "."), "a/");
+ assert.equal(libUtil.join("a/", ""), "a/");
+ assert.equal(libUtil.join("a/", "."), "a/");
+ assert.equal(libUtil.join("a//", ""), "a//");
+ assert.equal(libUtil.join("a//", "."), "a//");
+ assert.equal(libUtil.join("/a", ""), "/a/");
+ assert.equal(libUtil.join("/a", "."), "/a/");
+ assert.equal(libUtil.join("", ""), "");
+ assert.equal(libUtil.join(".", ""), "");
+ assert.equal(libUtil.join(".", ""), "");
+ assert.equal(libUtil.join(".", "."), "");
+ assert.equal(libUtil.join("..", ""), "../");
+ assert.equal(libUtil.join("..", "."), "../");
+ assert.equal(libUtil.join("http://foo.org/a", ""), "http://foo.org/a/");
+ assert.equal(libUtil.join("http://foo.org/a", "."), "http://foo.org/a/");
+ assert.equal(libUtil.join("http://foo.org/a/", ""), "http://foo.org/a/");
+ assert.equal(libUtil.join("http://foo.org/a/", "."), "http://foo.org/a/");
+ assert.equal(libUtil.join("http://foo.org/a//", ""), "http://foo.org/a//");
+ assert.equal(libUtil.join("http://foo.org/a//", "."), "http://foo.org/a//");
assert.equal(libUtil.join("http://foo.org", ""), "http://foo.org/");
assert.equal(libUtil.join("http://foo.org", "."), "http://foo.org/");
assert.equal(libUtil.join("http://foo.org/", ""), "http://foo.org/");
assert.equal(libUtil.join("http://foo.org/", "."), "http://foo.org/");
- assert.equal(libUtil.join("http://foo.org//", ""), "http://foo.org/");
- assert.equal(libUtil.join("http://foo.org//", "."), "http://foo.org/");
+ assert.equal(libUtil.join("http://foo.org//", ""), "http://foo.org//");
+ assert.equal(libUtil.join("http://foo.org//", "."), "http://foo.org//");
assert.equal(libUtil.join("//www.example.com", ""), "//www.example.com/");
assert.equal(libUtil.join("//www.example.com", "."), "//www.example.com/");
assert.equal(libUtil.join("http://foo.org/a", "b"), "http://foo.org/a/b");
assert.equal(libUtil.join("http://foo.org/a/", "b"), "http://foo.org/a/b");
- assert.equal(libUtil.join("http://foo.org/a//", "b"), "http://foo.org/a/b");
+ assert.equal(libUtil.join("http://foo.org/a//", "b"), "http://foo.org/a//b");
assert.equal(libUtil.join("http://foo.org/a", "b/"), "http://foo.org/a/b/");
- assert.equal(libUtil.join("http://foo.org/a", "b//"), "http://foo.org/a/b/");
+ assert.equal(libUtil.join("http://foo.org/a", "b//"), "http://foo.org/a/b//");
assert.equal(libUtil.join("http://foo.org/a/", "/b"), "http://foo.org/b");
- assert.equal(libUtil.join("http://foo.org/a//", "//b"), "http://b");
+ assert.equal(libUtil.join("http://foo.org/a//", "//b"), "http://b/");
assert.equal(libUtil.join("http://foo.org/a", ".."), "http://foo.org/");
assert.equal(libUtil.join("http://foo.org/a", "../b"), "http://foo.org/b");
assert.equal(libUtil.join("http://foo.org/a/b", "../c"), "http://foo.org/a/c");
- assert.equal(libUtil.join("http://foo.org/a", "."), "http://foo.org/a");
+ assert.equal(libUtil.join("http://foo.org/a", "."), "http://foo.org/a/");
assert.equal(libUtil.join("http://foo.org/a", "./b"), "http://foo.org/a/b");
assert.equal(libUtil.join("http://foo.org/a/b", "./c"), "http://foo.org/a/b/c");
- assert.equal(libUtil.join("http://foo.org/a", "http://www.example.com"), "http://www.example.com");
+ assert.equal(libUtil.join("http://foo.org/a", "http://www.example.com"), "http://www.example.com/");
assert.equal(libUtil.join("http://foo.org/a", "data:foo,bar"), "data:foo,bar");
assert.equal(libUtil.join("http://foo.org", "a"), "http://foo.org/a");
assert.equal(libUtil.join("http://foo.org/", "a"), "http://foo.org/a");
- assert.equal(libUtil.join("http://foo.org//", "a"), "http://foo.org/a");
+ assert.equal(libUtil.join("http://foo.org//", "a"), "http://foo.org//a");
assert.equal(libUtil.join("http://foo.org", "/a"), "http://foo.org/a");
assert.equal(libUtil.join("http://foo.org/", "/a"), "http://foo.org/a");
assert.equal(libUtil.join("http://foo.org//", "/a"), "http://foo.org/a");
-
- assert.equal(libUtil.join("http://", "www.example.com"), "http://www.example.com");
- assert.equal(libUtil.join("file:///", "www.example.com"), "file:///www.example.com");
- assert.equal(libUtil.join("http://", "ftp://example.com"), "ftp://example.com");
-
assert.equal(libUtil.join("http://www.example.com", "//foo.org/bar"), "http://foo.org/bar");
assert.equal(libUtil.join("//www.example.com", "//foo.org/bar"), "//foo.org/bar");
};
@@ -213,7 +170,7 @@ exports["test relative()"] = function(assert) {
assert.equal(libUtil.relative("http://the/root", "http://the/root/one.js"), "one.js");
assert.equal(libUtil.relative("/the/root", "/the/rootone.js"), "../rootone.js");
assert.equal(libUtil.relative("http://the/root", "http://the/rootone.js"), "../rootone.js");
- assert.equal(libUtil.relative("/the/root", "/therootone.js"), "/therootone.js");
+ assert.equal(libUtil.relative("/the/root", "/therootone.js"), "../../therootone.js");
assert.equal(libUtil.relative("http://the/root", "/therootone.js"), "/therootone.js");
assert.equal(libUtil.relative("", "/the/root/one.js"), "/the/root/one.js");
@@ -241,6 +198,8 @@ exports["test computeSourceURL"] = function(assert) {
"http://mozilla.com/src/test.js");
assert.equal(libUtil.computeSourceURL("", "test.js", "http://example.com/src/test.js.map"),
"http://example.com/src/test.js");
+ assert.equal(libUtil.computeSourceURL("", "/test.js", "http://example.com/src/test.js.map"),
+ "http://example.com/test.js");
// Legacy code won't pass in the sourceMapURL.
assert.equal(libUtil.computeSourceURL("", "src/test.js"), "src/test.js");