summaryrefslogtreecommitdiff
path: root/node_modules/webpack/lib/util/identifier.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/webpack/lib/util/identifier.js')
-rw-r--r--node_modules/webpack/lib/util/identifier.js381
1 files changed, 381 insertions, 0 deletions
diff --git a/node_modules/webpack/lib/util/identifier.js b/node_modules/webpack/lib/util/identifier.js
new file mode 100644
index 00000000..ea5ec2ea
--- /dev/null
+++ b/node_modules/webpack/lib/util/identifier.js
@@ -0,0 +1,381 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+*/
+
+"use strict";
+
+const path = require("path");
+
+const WINDOWS_ABS_PATH_REGEXP = /^[a-zA-Z]:[\\/]/;
+const SEGMENTS_SPLIT_REGEXP = /([|!])/;
+const WINDOWS_PATH_SEPARATOR_REGEXP = /\\/g;
+
+/**
+ * @typedef {Object} MakeRelativePathsCache
+ * @property {Map<string, Map<string, string>>=} relativePaths
+ */
+
+/**
+ * @param {string} relativePath relative path
+ * @returns {string} request
+ */
+const relativePathToRequest = relativePath => {
+ if (relativePath === "") return "./.";
+ if (relativePath === "..") return "../.";
+ if (relativePath.startsWith("../")) return relativePath;
+ return `./${relativePath}`;
+};
+
+/**
+ * @param {string} context context for relative path
+ * @param {string} maybeAbsolutePath path to make relative
+ * @returns {string} relative path in request style
+ */
+const absoluteToRequest = (context, maybeAbsolutePath) => {
+ if (maybeAbsolutePath[0] === "/") {
+ if (
+ maybeAbsolutePath.length > 1 &&
+ maybeAbsolutePath[maybeAbsolutePath.length - 1] === "/"
+ ) {
+ // this 'path' is actually a regexp generated by dynamic requires.
+ // Don't treat it as an absolute path.
+ return maybeAbsolutePath;
+ }
+
+ const querySplitPos = maybeAbsolutePath.indexOf("?");
+ let resource =
+ querySplitPos === -1
+ ? maybeAbsolutePath
+ : maybeAbsolutePath.slice(0, querySplitPos);
+ resource = relativePathToRequest(path.posix.relative(context, resource));
+ return querySplitPos === -1
+ ? resource
+ : resource + maybeAbsolutePath.slice(querySplitPos);
+ }
+
+ if (WINDOWS_ABS_PATH_REGEXP.test(maybeAbsolutePath)) {
+ const querySplitPos = maybeAbsolutePath.indexOf("?");
+ let resource =
+ querySplitPos === -1
+ ? maybeAbsolutePath
+ : maybeAbsolutePath.slice(0, querySplitPos);
+ resource = path.win32.relative(context, resource);
+ if (!WINDOWS_ABS_PATH_REGEXP.test(resource)) {
+ resource = relativePathToRequest(
+ resource.replace(WINDOWS_PATH_SEPARATOR_REGEXP, "/")
+ );
+ }
+ return querySplitPos === -1
+ ? resource
+ : resource + maybeAbsolutePath.slice(querySplitPos);
+ }
+
+ // not an absolute path
+ return maybeAbsolutePath;
+};
+
+/**
+ * @param {string} context context for relative path
+ * @param {string} relativePath path
+ * @returns {string} absolute path
+ */
+const requestToAbsolute = (context, relativePath) => {
+ if (relativePath.startsWith("./") || relativePath.startsWith("../"))
+ return path.join(context, relativePath);
+ return relativePath;
+};
+
+const makeCacheable = realFn => {
+ /** @type {WeakMap<object, Map<string, ParsedResource>>} */
+ const cache = new WeakMap();
+
+ const getCache = associatedObjectForCache => {
+ const entry = cache.get(associatedObjectForCache);
+ if (entry !== undefined) return entry;
+ /** @type {Map<string, ParsedResource>} */
+ const map = new Map();
+ cache.set(associatedObjectForCache, map);
+ return map;
+ };
+
+ /**
+ * @param {string} str the path with query and fragment
+ * @param {Object=} associatedObjectForCache an object to which the cache will be attached
+ * @returns {ParsedResource} parsed parts
+ */
+ const fn = (str, associatedObjectForCache) => {
+ if (!associatedObjectForCache) return realFn(str);
+ const cache = getCache(associatedObjectForCache);
+ const entry = cache.get(str);
+ if (entry !== undefined) return entry;
+ const result = realFn(str);
+ cache.set(str, result);
+ return result;
+ };
+
+ fn.bindCache = associatedObjectForCache => {
+ const cache = getCache(associatedObjectForCache);
+ return str => {
+ const entry = cache.get(str);
+ if (entry !== undefined) return entry;
+ const result = realFn(str);
+ cache.set(str, result);
+ return result;
+ };
+ };
+
+ return fn;
+};
+
+const makeCacheableWithContext = fn => {
+ /** @type {WeakMap<object, Map<string, Map<string, string>>>} */
+ const cache = new WeakMap();
+
+ /**
+ * @param {string} context context used to create relative path
+ * @param {string} identifier identifier used to create relative path
+ * @param {Object=} associatedObjectForCache an object to which the cache will be attached
+ * @returns {string} the returned relative path
+ */
+ const cachedFn = (context, identifier, associatedObjectForCache) => {
+ if (!associatedObjectForCache) return fn(context, identifier);
+
+ let innerCache = cache.get(associatedObjectForCache);
+ if (innerCache === undefined) {
+ innerCache = new Map();
+ cache.set(associatedObjectForCache, innerCache);
+ }
+
+ let cachedResult;
+ let innerSubCache = innerCache.get(context);
+ if (innerSubCache === undefined) {
+ innerCache.set(context, (innerSubCache = new Map()));
+ } else {
+ cachedResult = innerSubCache.get(identifier);
+ }
+
+ if (cachedResult !== undefined) {
+ return cachedResult;
+ } else {
+ const result = fn(context, identifier);
+ innerSubCache.set(identifier, result);
+ return result;
+ }
+ };
+
+ /**
+ * @param {Object=} associatedObjectForCache an object to which the cache will be attached
+ * @returns {function(string, string): string} cached function
+ */
+ cachedFn.bindCache = associatedObjectForCache => {
+ let innerCache;
+ if (associatedObjectForCache) {
+ innerCache = cache.get(associatedObjectForCache);
+ if (innerCache === undefined) {
+ innerCache = new Map();
+ cache.set(associatedObjectForCache, innerCache);
+ }
+ } else {
+ innerCache = new Map();
+ }
+
+ /**
+ * @param {string} context context used to create relative path
+ * @param {string} identifier identifier used to create relative path
+ * @returns {string} the returned relative path
+ */
+ const boundFn = (context, identifier) => {
+ let cachedResult;
+ let innerSubCache = innerCache.get(context);
+ if (innerSubCache === undefined) {
+ innerCache.set(context, (innerSubCache = new Map()));
+ } else {
+ cachedResult = innerSubCache.get(identifier);
+ }
+
+ if (cachedResult !== undefined) {
+ return cachedResult;
+ } else {
+ const result = fn(context, identifier);
+ innerSubCache.set(identifier, result);
+ return result;
+ }
+ };
+
+ return boundFn;
+ };
+
+ /**
+ * @param {string} context context used to create relative path
+ * @param {Object=} associatedObjectForCache an object to which the cache will be attached
+ * @returns {function(string): string} cached function
+ */
+ cachedFn.bindContextCache = (context, associatedObjectForCache) => {
+ let innerSubCache;
+ if (associatedObjectForCache) {
+ let innerCache = cache.get(associatedObjectForCache);
+ if (innerCache === undefined) {
+ innerCache = new Map();
+ cache.set(associatedObjectForCache, innerCache);
+ }
+
+ innerSubCache = innerCache.get(context);
+ if (innerSubCache === undefined) {
+ innerCache.set(context, (innerSubCache = new Map()));
+ }
+ } else {
+ innerSubCache = new Map();
+ }
+
+ /**
+ * @param {string} identifier identifier used to create relative path
+ * @returns {string} the returned relative path
+ */
+ const boundFn = identifier => {
+ const cachedResult = innerSubCache.get(identifier);
+ if (cachedResult !== undefined) {
+ return cachedResult;
+ } else {
+ const result = fn(context, identifier);
+ innerSubCache.set(identifier, result);
+ return result;
+ }
+ };
+
+ return boundFn;
+ };
+
+ return cachedFn;
+};
+
+/**
+ *
+ * @param {string} context context for relative path
+ * @param {string} identifier identifier for path
+ * @returns {string} a converted relative path
+ */
+const _makePathsRelative = (context, identifier) => {
+ return identifier
+ .split(SEGMENTS_SPLIT_REGEXP)
+ .map(str => absoluteToRequest(context, str))
+ .join("");
+};
+
+exports.makePathsRelative = makeCacheableWithContext(_makePathsRelative);
+
+/**
+ *
+ * @param {string} context context for relative path
+ * @param {string} identifier identifier for path
+ * @returns {string} a converted relative path
+ */
+const _makePathsAbsolute = (context, identifier) => {
+ return identifier
+ .split(SEGMENTS_SPLIT_REGEXP)
+ .map(str => requestToAbsolute(context, str))
+ .join("");
+};
+
+exports.makePathsAbsolute = makeCacheableWithContext(_makePathsAbsolute);
+
+/**
+ * @param {string} context absolute context path
+ * @param {string} request any request string may containing absolute paths, query string, etc.
+ * @returns {string} a new request string avoiding absolute paths when possible
+ */
+const _contextify = (context, request) => {
+ return request
+ .split("!")
+ .map(r => absoluteToRequest(context, r))
+ .join("!");
+};
+
+const contextify = makeCacheableWithContext(_contextify);
+exports.contextify = contextify;
+
+/**
+ * @param {string} context absolute context path
+ * @param {string} request any request string
+ * @returns {string} a new request string using absolute paths when possible
+ */
+const _absolutify = (context, request) => {
+ return request
+ .split("!")
+ .map(r => requestToAbsolute(context, r))
+ .join("!");
+};
+
+const absolutify = makeCacheableWithContext(_absolutify);
+exports.absolutify = absolutify;
+
+const PATH_QUERY_FRAGMENT_REGEXP =
+ /^((?:\0.|[^?#\0])*)(\?(?:\0.|[^#\0])*)?(#.*)?$/;
+const PATH_QUERY_REGEXP = /^((?:\0.|[^?\0])*)(\?.*)?$/;
+
+/** @typedef {{ resource: string, path: string, query: string, fragment: string }} ParsedResource */
+/** @typedef {{ resource: string, path: string, query: string }} ParsedResourceWithoutFragment */
+
+/**
+ * @param {string} str the path with query and fragment
+ * @returns {ParsedResource} parsed parts
+ */
+const _parseResource = str => {
+ const match = PATH_QUERY_FRAGMENT_REGEXP.exec(str);
+ return {
+ resource: str,
+ path: match[1].replace(/\0(.)/g, "$1"),
+ query: match[2] ? match[2].replace(/\0(.)/g, "$1") : "",
+ fragment: match[3] || ""
+ };
+};
+exports.parseResource = makeCacheable(_parseResource);
+
+/**
+ * Parse resource, skips fragment part
+ * @param {string} str the path with query and fragment
+ * @returns {ParsedResourceWithoutFragment} parsed parts
+ */
+const _parseResourceWithoutFragment = str => {
+ const match = PATH_QUERY_REGEXP.exec(str);
+ return {
+ resource: str,
+ path: match[1].replace(/\0(.)/g, "$1"),
+ query: match[2] ? match[2].replace(/\0(.)/g, "$1") : ""
+ };
+};
+exports.parseResourceWithoutFragment = makeCacheable(
+ _parseResourceWithoutFragment
+);
+
+/**
+ * @param {string} filename the filename which should be undone
+ * @param {string} outputPath the output path that is restored (only relevant when filename contains "..")
+ * @param {boolean} enforceRelative true returns ./ for empty paths
+ * @returns {string} repeated ../ to leave the directory of the provided filename to be back on output dir
+ */
+exports.getUndoPath = (filename, outputPath, enforceRelative) => {
+ let depth = -1;
+ let append = "";
+ outputPath = outputPath.replace(/[\\/]$/, "");
+ for (const part of filename.split(/[/\\]+/)) {
+ if (part === "..") {
+ if (depth > -1) {
+ depth--;
+ } else {
+ const i = outputPath.lastIndexOf("/");
+ const j = outputPath.lastIndexOf("\\");
+ const pos = i < 0 ? j : j < 0 ? i : Math.max(i, j);
+ if (pos < 0) return outputPath + "/";
+ append = outputPath.slice(pos + 1) + "/" + append;
+ outputPath = outputPath.slice(0, pos);
+ }
+ } else if (part !== ".") {
+ depth++;
+ }
+ }
+ return depth > 0
+ ? `${"../".repeat(depth)}${append}`
+ : enforceRelative
+ ? `./${append}`
+ : append;
+};