generated from dellevin/template
1
This commit is contained in:
318
node_modules/webpack/lib/util/extractSourceMap.js
generated
vendored
Normal file
318
node_modules/webpack/lib/util/extractSourceMap.js
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||
Author Natsu @xiaoxiaojx
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
const urlUtils = require("url");
|
||||
const { isAbsolute, join } = require("./fs");
|
||||
|
||||
/** @typedef {import("./fs").InputFileSystem} InputFileSystem */
|
||||
/** @typedef {string | Buffer<ArrayBufferLike>} StringOrBuffer */
|
||||
/** @typedef {(input: StringOrBuffer, resourcePath: string, fs: InputFileSystem) => Promise<{ source: StringOrBuffer, sourceMap: string | RawSourceMap | undefined, fileDependencies: string[] }>} SourceMapExtractorFunction */
|
||||
/** @typedef {import("webpack-sources").RawSourceMap} RawSourceMap */
|
||||
/** @typedef {(resourcePath: string) => Promise<StringOrBuffer>} ReadResource */
|
||||
|
||||
/**
|
||||
* @typedef {object} SourceMappingURL
|
||||
* @property {string} sourceMappingURL
|
||||
* @property {string} replacementString
|
||||
*/
|
||||
|
||||
// Matches only the last occurrence of sourceMappingURL
|
||||
const innerRegex = /\s*[#@]\s*sourceMappingURL\s*=\s*([^\s'"]*)\s*/;
|
||||
|
||||
const validProtocolPattern = /^[a-z][a-z0-9+.-]*:/i;
|
||||
|
||||
const sourceMappingURLRegex = new RegExp(
|
||||
"(?:" +
|
||||
"/\\*" +
|
||||
"(?:\\s*\r?\n(?://)?)?" +
|
||||
`(?:${innerRegex.source})` +
|
||||
"\\s*" +
|
||||
"\\*/" +
|
||||
"|" +
|
||||
`//(?:${innerRegex.source})` +
|
||||
")" +
|
||||
"\\s*"
|
||||
);
|
||||
|
||||
/**
|
||||
* Extract source mapping URL from code comments
|
||||
* @param {string} code source code content
|
||||
* @returns {SourceMappingURL} source mapping information
|
||||
*/
|
||||
function getSourceMappingURL(code) {
|
||||
const lines = code.split(/^/m);
|
||||
/** @type {RegExpMatchArray | null | undefined} */
|
||||
let match;
|
||||
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
match = lines[i].match(sourceMappingURLRegex);
|
||||
if (match) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const sourceMappingURL = match ? match[1] || match[2] || "" : "";
|
||||
|
||||
return {
|
||||
sourceMappingURL: sourceMappingURL
|
||||
? decodeURI(sourceMappingURL)
|
||||
: sourceMappingURL,
|
||||
replacementString: match ? match[0] : ""
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get absolute path for source file
|
||||
* @param {string} context context directory
|
||||
* @param {string} request file request
|
||||
* @param {string} sourceRoot source root directory
|
||||
* @returns {string} absolute path
|
||||
*/
|
||||
function getAbsolutePath(context, request, sourceRoot) {
|
||||
if (sourceRoot) {
|
||||
if (isAbsolute(sourceRoot)) {
|
||||
return join(undefined, sourceRoot, request);
|
||||
}
|
||||
|
||||
return join(undefined, join(undefined, context, sourceRoot), request);
|
||||
}
|
||||
|
||||
return join(undefined, context, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if value is a URL
|
||||
* @param {string} value string to check
|
||||
* @returns {boolean} true if value is a URL
|
||||
*/
|
||||
function isURL(value) {
|
||||
return validProtocolPattern.test(value) && !path.win32.isAbsolute(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch from multiple possible file paths
|
||||
* @param {ReadResource} readResource read resource function
|
||||
* @param {string[]} possibleRequests array of possible file paths
|
||||
* @param {string} errorsAccumulator accumulated error messages
|
||||
* @returns {Promise<{ path: string, data?: string }>} source content promise
|
||||
*/
|
||||
async function fetchPathsFromURL(
|
||||
readResource,
|
||||
possibleRequests,
|
||||
errorsAccumulator = ""
|
||||
) {
|
||||
/** @type {StringOrBuffer} */
|
||||
let result;
|
||||
|
||||
try {
|
||||
result = await readResource(possibleRequests[0]);
|
||||
} catch (error) {
|
||||
errorsAccumulator += `${/** @type {Error} */ (error).message}\n\n`;
|
||||
|
||||
const [, ...tailPossibleRequests] = possibleRequests;
|
||||
|
||||
if (tailPossibleRequests.length === 0) {
|
||||
/** @type {Error} */ (error).message = errorsAccumulator;
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return fetchPathsFromURL(
|
||||
readResource,
|
||||
tailPossibleRequests,
|
||||
errorsAccumulator
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
path: possibleRequests[0],
|
||||
data: result.toString("utf8")
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch source content from URL
|
||||
* @param {ReadResource} readResource The read resource function
|
||||
* @param {string} context context directory
|
||||
* @param {string} url source URL
|
||||
* @param {string=} sourceRoot source root directory
|
||||
* @param {boolean=} skipReading whether to skip reading file content
|
||||
* @returns {Promise<{ sourceURL: string, sourceContent?: StringOrBuffer }>} source content promise
|
||||
*/
|
||||
async function fetchFromURL(
|
||||
readResource,
|
||||
context,
|
||||
url,
|
||||
sourceRoot,
|
||||
skipReading = false
|
||||
) {
|
||||
// 1. It's an absolute url and it is not `windows` path like `C:\dir\file`
|
||||
if (isURL(url)) {
|
||||
// eslint-disable-next-line n/no-deprecated-api
|
||||
const { protocol } = urlUtils.parse(url);
|
||||
if (protocol === "data:") {
|
||||
const sourceContent = skipReading ? "" : await readResource(url);
|
||||
|
||||
return { sourceURL: "", sourceContent };
|
||||
}
|
||||
|
||||
if (protocol === "file:") {
|
||||
const pathFromURL = urlUtils.fileURLToPath(url);
|
||||
const sourceURL = path.normalize(pathFromURL);
|
||||
const sourceContent = skipReading ? "" : await readResource(sourceURL);
|
||||
|
||||
return { sourceURL, sourceContent };
|
||||
}
|
||||
|
||||
const sourceContent = skipReading ? "" : await readResource(url);
|
||||
return { sourceURL: url, sourceContent };
|
||||
}
|
||||
|
||||
// 3. Absolute path
|
||||
if (isAbsolute(url)) {
|
||||
let sourceURL = path.normalize(url);
|
||||
|
||||
/** @type {undefined | StringOrBuffer} */
|
||||
let sourceContent;
|
||||
|
||||
if (!skipReading) {
|
||||
/** @type {string[]} */
|
||||
const possibleRequests = [sourceURL];
|
||||
|
||||
if (url.startsWith("/")) {
|
||||
possibleRequests.push(
|
||||
getAbsolutePath(context, sourceURL.slice(1), sourceRoot || "")
|
||||
);
|
||||
}
|
||||
|
||||
const result = await fetchPathsFromURL(readResource, possibleRequests);
|
||||
|
||||
sourceURL = result.path;
|
||||
sourceContent = result.data;
|
||||
}
|
||||
|
||||
return { sourceURL, sourceContent };
|
||||
}
|
||||
|
||||
// 4. Relative path
|
||||
const sourceURL = getAbsolutePath(context, url, sourceRoot || "");
|
||||
/** @type {undefined | StringOrBuffer} */
|
||||
let sourceContent;
|
||||
|
||||
if (!skipReading) {
|
||||
sourceContent = await readResource(sourceURL);
|
||||
}
|
||||
|
||||
return { sourceURL, sourceContent };
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract source map from code content
|
||||
* @param {StringOrBuffer} stringOrBuffer The input code content as string or buffer
|
||||
* @param {string} resourcePath The path to the resource file
|
||||
* @param {ReadResource} readResource The read resource function
|
||||
* @returns {Promise<{ source: StringOrBuffer, sourceMap: string | RawSourceMap | undefined }>} Promise resolving to extracted source map information
|
||||
*/
|
||||
async function extractSourceMap(stringOrBuffer, resourcePath, readResource) {
|
||||
const input =
|
||||
typeof stringOrBuffer === "string"
|
||||
? stringOrBuffer
|
||||
: stringOrBuffer.toString("utf8");
|
||||
const inputSourceMap = undefined;
|
||||
const output = {
|
||||
source: stringOrBuffer,
|
||||
sourceMap: inputSourceMap
|
||||
};
|
||||
const { sourceMappingURL, replacementString } = getSourceMappingURL(input);
|
||||
|
||||
if (!sourceMappingURL) {
|
||||
return output;
|
||||
}
|
||||
|
||||
const baseContext = path.dirname(resourcePath);
|
||||
|
||||
const { sourceURL, sourceContent } = await fetchFromURL(
|
||||
readResource,
|
||||
baseContext,
|
||||
sourceMappingURL
|
||||
);
|
||||
|
||||
if (!sourceContent) {
|
||||
return output;
|
||||
}
|
||||
|
||||
/** @type {RawSourceMap} */
|
||||
const map = JSON.parse(
|
||||
sourceContent.toString("utf8").replace(/^\)\]\}'/, "")
|
||||
);
|
||||
|
||||
const context = sourceURL ? path.dirname(sourceURL) : baseContext;
|
||||
|
||||
const resolvedSources = await Promise.all(
|
||||
map.sources.map(
|
||||
async (/** @type {string} */ source, /** @type {number} */ i) => {
|
||||
const originalSourceContent =
|
||||
map.sourcesContent &&
|
||||
typeof map.sourcesContent[i] !== "undefined" &&
|
||||
map.sourcesContent[i] !== null
|
||||
? map.sourcesContent[i]
|
||||
: undefined;
|
||||
const skipReading = typeof originalSourceContent !== "undefined";
|
||||
// We do not skipReading here, because we need absolute paths in sources.
|
||||
// This is necessary so that for sourceMaps with the same file structure in sources, name collisions do not occur.
|
||||
// https://github.com/webpack-contrib/source-map-loader/issues/51
|
||||
let { sourceURL, sourceContent } = await fetchFromURL(
|
||||
readResource,
|
||||
context,
|
||||
source,
|
||||
map.sourceRoot,
|
||||
skipReading
|
||||
);
|
||||
|
||||
if (skipReading) {
|
||||
sourceContent = originalSourceContent;
|
||||
}
|
||||
|
||||
// Return original value of `source` when error happens
|
||||
return { sourceURL, sourceContent };
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
/** @type {RawSourceMap} */
|
||||
const newMap = { ...map };
|
||||
|
||||
newMap.sources = [];
|
||||
newMap.sourcesContent = [];
|
||||
|
||||
delete newMap.sourceRoot;
|
||||
|
||||
for (const source of resolvedSources) {
|
||||
const { sourceURL, sourceContent } = source;
|
||||
|
||||
newMap.sources.push(sourceURL || "");
|
||||
newMap.sourcesContent.push(
|
||||
sourceContent ? sourceContent.toString("utf8") : ""
|
||||
);
|
||||
}
|
||||
|
||||
const sourcesContentIsEmpty =
|
||||
newMap.sourcesContent.filter(Boolean).length === 0;
|
||||
|
||||
if (sourcesContentIsEmpty) {
|
||||
delete newMap.sourcesContent;
|
||||
}
|
||||
|
||||
return {
|
||||
source: input.replace(replacementString, ""),
|
||||
sourceMap: /** @type {RawSourceMap} */ (newMap)
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = extractSourceMap;
|
||||
module.exports.getSourceMappingURL = getSourceMappingURL;
|
||||
Reference in New Issue
Block a user