UNPKG

stylus-loader

Version:
808 lines (742 loc) 29.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createEvaluator = createEvaluator; exports.getStylusImplementation = getStylusImplementation; exports.getStylusOptions = getStylusOptions; exports.normalizeSourceMap = normalizeSourceMap; exports.readFile = readFile; exports.resolveFilename = resolveFilename; exports.urlResolver = urlResolver; var _nodePath = _interopRequireDefault(require("node:path")); var _nodeUrl = require("node:url"); var _normalizePath = _interopRequireDefault(require("normalize-path")); var _stylus = _interopRequireDefault(require("stylus")); var _depsResolver = _interopRequireDefault(require("stylus/lib/visitor/deps-resolver.js")); var _tinyglobby = require("tinyglobby"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // eslint-disable-next-line n/no-deprecated-api // @ts-expect-error no types are shipped for this internal entry point // eslint-disable-next-line jsdoc/reject-any-type /** @typedef {any} EXPECTED_ANY */ const { Compiler: StylusCompiler, Evaluator: StylusEvaluator, Parser: StylusParser, nodes, utils } = _stylus.default; const Compiler = StylusCompiler; const Evaluator = StylusEvaluator; const Parser = StylusParser; /** @typedef {import("webpack").LoaderContext<LoaderOptions>} LoaderContext */ /** @typedef {import("stylus/lib/renderer.js")} Renderer */ /** @typedef {import("stylus").RenderOptions} RenderOptions */ /** * @typedef {object} StylusResolveUrlOptions * @property {boolean=} nocheck true when no need to check on disk, otherwise false * @property {string[]=} paths additional paths */ /** * @callback StylusPluginFn * @param {object} renderer renderer * @returns {void} */ /** * @typedef {object} StylusSourceMapOptions * @property {boolean=} comment append the source map URL comment to the CSS. * @property {string=} sourceRoot root URL for the source files. * @property {string=} basePath base path to resolve relative source map paths. * @property {boolean=} inline embed the source map directly into the CSS as Base64. */ /** * @typedef {object} NoTypesStylusOptions * @property {string=} dest destination * @property {string | StylusPluginFn | (string | StylusPluginFn)[]=} use stylus plugins * @property {string[]=} import files to import * @property {string[]=} include include paths * @property {boolean=} compress true if compressed output, otherwise false * @property {boolean=} includeCSS true if include CSS on `@import`, otherwise false * @property {boolean=} hoistAtrules true if hoist at-rules, otherwise false * @property {boolean=} lineNumbers true if line numbers are emitted, otherwise false * @property {boolean=} disableCache true if cache is disabled, otherwise false * @property {boolean | StylusSourceMapOptions=} sourcemap source map * @property {boolean | StylusResolveUrlOptions=} resolveURL `resolveURL` options * @property {(Record<string, EXPECTED_ANY> | ([string, EXPECTED_ANY] | [string, EXPECTED_ANY, boolean])[])=} define list of definitions * @property {{ path: string }[]=} _imports list of imports */ /** @typedef {RenderOptions & NoTypesStylusOptions} StylusOptions */ /** * @typedef {object} LoaderOptions * @property {(string | ((source: string, options: StylusOptions) => Renderer))=} implementation stylus implementation * @property {(StylusOptions | ((loaderContext: LoaderContext) => StylusOptions))=} stylusOptions stylus options * @property {boolean=} sourceMap true if source map is enabled, otherwise false * @property {boolean=} webpackImporter true if webpack importer is enabled, otherwise false * @property {(string | ((content: string, loaderContext: LoaderContext) => string | Promise<string>))=} additionalData prepends/appends `Stylus` code to the actual entry file */ /** @typedef {Error & { filename?: string }} StylusError */ /** * @typedef {object} RawSourceMap * @property {number} version version * @property {string[]} sources sources * @property {string[]} names names * @property {string=} sourceRoot source root * @property {string[]=} sourcesContent sources content * @property {string} mappings mappings * @property {string=} file file */ /** @typedef {(context: string, request: string) => Promise<string>} Resolver */ /** * Extracts the non-glob base directory from a glob pattern. * Replaces fast-glob's generateTasks()[0].base. * @param {string} pattern a glob pattern * @returns {string} the static base path, or "." if there is none */ function getGlobBase(pattern) { const parts = pattern.split("/"); const base = []; for (const part of parts) { if ((0, _tinyglobby.isDynamicPattern)(part)) { break; } base.push(part); } return base.join("/") || "."; } // Examples: // - ~package // - ~package/ // - ~@org // - ~@org/ // - ~@org/package // - ~@org/package/ const IS_MODULE_IMPORT = /^~([^/]+|[^/]+\/|@[^/]+[/][^/]+|@[^/]+\/?|@[^/]+[/][^/]+\/)$/; const MODULE_REQUEST_REGEX = /^[^?]*~/; /** * @param {LoaderContext} loaderContext loader context * @returns {boolean} true when mode is production, otherwise false */ function isProductionLikeMode(loaderContext) { return loaderContext.mode === "production" || !loaderContext.mode; } /** * Derives the stylus options from the loader context and normalizes its values with sane defaults. * @param {LoaderContext} loaderContext loader context * @param {LoaderOptions} loaderOptions loader options * @returns {Promise<StylusOptions>} stylus options */ async function getStylusOptions(loaderContext, loaderOptions) { /** @type {StylusOptions} */ const options = typeof loaderOptions.stylusOptions === "function" ? loaderOptions.stylusOptions(loaderContext) || {} : loaderOptions.stylusOptions || {}; /** @type {StylusOptions} */ const stylusOptions = { filename: loaderContext.resourcePath, dest: _nodePath.default.dirname(loaderContext.resourcePath), ...options, // Keep track of imported files (used by Stylus CLI watch mode) // Don't allow to override, because it is internally _imports: [] }; if (typeof stylusOptions.use !== "undefined") { const resolve = loaderContext.getResolve({ dependencyType: "stylus", conditionNames: ["..."], mainFields: ["main", "..."], mainFiles: ["index", "..."], extensions: [".js", ".cjs", ".mjs"], preferRelative: true }); stylusOptions.use = await Promise.all((Array.isArray(stylusOptions.use) ? stylusOptions.use : [stylusOptions.use]).map(async item => { if (typeof item === "string") { try { const resolved = await resolve(loaderContext.context, item); loaderContext.addBuildDependency(resolved); const { default: plugin } = await import((0, _nodeUrl.pathToFileURL)(resolved).href); return plugin(stylusOptions); } catch (error) { throw new Error(`Failed to load "${item}" Stylus plugin. Are you sure it's installed?\n${error}`, { cause: error }); } } return item; })); } // https://github.com/stylus/stylus/issues/2119 stylusOptions.resolveURL = typeof stylusOptions.resolveURL === "boolean" && !stylusOptions.resolveURL ? false : typeof stylusOptions.resolveURL === "object" ? { ...stylusOptions.resolveURL } : { nocheck: true }; if (typeof stylusOptions.compress === "undefined" && isProductionLikeMode(loaderContext)) { stylusOptions.compress = true; } return stylusOptions; } /** * This function is not Webpack-specific and can be used by tools wishing to mimic `stylus-loader`'s behaviour, so its signature should not be changed. * @param {LoaderContext} loaderContext loader context * @param {LoaderOptions["implementation"]} implementation stylus implementation * @returns {Promise<(source: string, options: StylusOptions) => Renderer>} resolved stylus implementation */ async function getStylusImplementation(loaderContext, implementation) { if (!implementation || typeof implementation === "string") { const specifier = implementation || "stylus"; const { default: resolved } = await import(_nodePath.default.isAbsolute(specifier) ? (0, _nodeUrl.pathToFileURL)(specifier).href : specifier); return resolved; } return implementation; } /** * @param {LoaderContext} loaderContext loader context * @param {string} filename filename * @returns {string[]} possible requests */ function getPossibleRequests(loaderContext, filename) { let request = filename; // A `~` makes the url an module if (MODULE_REQUEST_REGEX.test(filename)) { request = request.replace(MODULE_REQUEST_REGEX, ""); } if (IS_MODULE_IMPORT.test(filename)) { request = request[request.length - 1] === "/" ? request : `${request}/`; } return [...new Set([request, filename])]; } /** * @param {string} context context * @param {string[]} possibleRequests possible requests * @param {Resolver} resolve resolver * @returns {Promise<string>} resolved request */ async function resolveRequests(context, possibleRequests, resolve) { if (possibleRequests.length === 0) { throw new Error("Not found"); } let result; try { result = await resolve(context, possibleRequests[0]); } catch (error) { const [, ...tailPossibleRequests] = possibleRequests; if (tailPossibleRequests.length === 0) { throw error; } result = await resolveRequests(context, tailPossibleRequests, resolve); } return result; } /** * @param {LoaderContext} loaderContext loader context * @param {Resolver} fileResolver file resolver * @param {Resolver} globResolver glob resolver * @param {boolean} isGlob true when filename is a glob pattern, otherwise false * @param {string} context context * @param {string} filename filename * @returns {Promise<string | string[]>} resolved filename or list of files (when glob) */ async function resolveFilename(loaderContext, fileResolver, globResolver, isGlob, context, filename) { const possibleRequests = getPossibleRequests(loaderContext, filename); let result; try { result = await resolveRequests(context, possibleRequests, fileResolver); } catch (error) { if (isGlob) { const globBase = getGlobBase(filename); if (globBase === ".") { throw new Error('Glob resolving without a glob base ("~**/*") is not supported, please specify a glob base ("~package/**/*")', { cause: error }); } const possibleGlobRequests = getPossibleRequests(loaderContext, globBase); const globResult = await resolveRequests(context, possibleGlobRequests, globResolver); loaderContext.addContextDependency(globResult); const patterns = filename.replace(new RegExp(`^${globBase}`), (0, _normalizePath.default)(globResult)); const paths = await (0, _tinyglobby.glob)([patterns], { absolute: true, cwd: globResult }); return paths.toSorted().filter(file => /\.styl$/i.test(file)); } throw error; } return result; } /** * @param {LoaderContext["fs"]} inputFileSystem input file system * @param {string} filepath file path * @returns {Promise<Buffer>} file contents */ function readFile(inputFileSystem, filepath) { return new Promise((resolve, reject) => { inputFileSystem.readFile(filepath, (error, stats) => { if (error) { reject(error); } resolve(/** @type {Buffer} */stats); }); }); } const URL_RE = /^(?:url\s*\(\s*)?['"]?(?:[#/]|(?:https?:)?\/\/)/i; /** * @typedef {object} Dependency * @property {number} originalLineno original line number * @property {number} originalColumn original column * @property {string} originalNodePath original node path * @property {undefined | string | string[] | Promise<string | string[]>} resolved resolved path(s) * @property {Error=} error resolve error, when failed */ /** * @param {Map<string, Dependency[]>} resolvedDependencies resolved dependencies * @param {LoaderContext} loaderContext loader context * @param {Resolver} fileResolver file resolver * @param {Resolver} globResolver glob resolver * @param {Set<string>} seen seen files * @param {string} code code * @param {string} filename filename * @param {StylusOptions} options stylus options * @returns {Promise<void>} resolves once dependencies have been collected */ async function getDependencies(resolvedDependencies, loaderContext, fileResolver, globResolver, seen, code, filename, options) { seen.add(filename); // See https://github.com/stylus/stylus/issues/2108 const newOptions = { ...options, filename, cache: false }; // @ts-expect-error no types are shipped const parser = new Parser(code, newOptions); /** @type {EXPECTED_ANY} */ let ast; try { // @ts-expect-error no types are shipped ast = parser.parse(); } catch (error) { loaderContext.emitError(/** @type {Error} */error); return; } /** @type {(Dependency & { resolved?: undefined | string | string[] | Promise<string | string[]> })[]} */ const dependencies = []; class ImportVisitor extends _depsResolver.default { /** * @param {import("stylus").nodes.Import} node import node * @returns {void} */ visitImport(node) { let firstNode = node.path.first; if (/** @type {import("stylus").nodes.Call} */ firstNode.name === "url") { return; } if (!(/** @type {import("stylus").nodes.String} */firstNode.val)) { // @ts-expect-error no types are shipped for this const evaluator = new Evaluator(ast); // @ts-expect-error no types are shipped for this firstNode = evaluator.visit(firstNode).first; } const originalNodePath = // @ts-expect-error bad types !firstNode.val.isNull && /** @type {import("stylus").nodes.String} */firstNode.val || /** @type {import("stylus").nodes.Ident} */ firstNode.name; let nodePath = originalNodePath; if (!nodePath) { return; } let found; let oldNodePath; const literal = /\.css(?:"|$)/.test(nodePath); if (!literal && !/\.styl$/i.test(nodePath)) { oldNodePath = nodePath; nodePath += ".styl"; } const isGlob = (0, _tinyglobby.isDynamicPattern)(nodePath); // @ts-expect-error no types are shipped for this let { filename, paths } = this; if (_nodePath.default.sep === "\\") { filename = filename.replace(/^\\\\\?\\/, ""); paths = paths.map((/** @type {string} */item) => item.replace(/^\\\\\?\\/, "")); } found = utils.find(nodePath, paths, filename); if (found && _nodePath.default.sep === "\\") { found = found.map((/** @type {string} */item) => item.replace(/^\/\/\?\//, "")); } if (found && isGlob) { const globBase = getGlobBase(nodePath); const context = globBase === "." ? _nodePath.default.dirname(filename) : _nodePath.default.join(_nodePath.default.dirname(filename), globBase); loaderContext.addContextDependency(context); } if (!found && oldNodePath) { found = utils.lookupIndex(oldNodePath, paths, filename); if (found && _nodePath.default.sep === "\\") { found = found.map((/** @type {string} */item) => item.replace(/^\/\/\?\//, "")); } } if (found) { dependencies.push({ originalLineno: firstNode.lineno, originalColumn: firstNode.column, originalNodePath, resolved: found.map((/** @type {string} */item) => _nodePath.default.isAbsolute(item) ? item : _nodePath.default.join(process.cwd(), item)) }); return; } dependencies.push({ originalLineno: firstNode.lineno, originalColumn: firstNode.column, originalNodePath, resolved: resolveFilename(loaderContext, fileResolver, globResolver, isGlob, _nodePath.default.dirname(filename), originalNodePath) }); } } // @ts-expect-error no types are shipped for this const visitor = new ImportVisitor(ast, newOptions); // @ts-expect-error no types are shipped for this visitor.visit(ast); await Promise.all([...dependencies].map(async result => { let { resolved } = result; try { resolved = await resolved; } catch (err) { const r = result; delete r.resolved; result.error = /** @type {Error} */err; return; } const isArray = Array.isArray(resolved); // `stylus` returns forward slashes on windows result.resolved = isArray ? /** @type {string[]} */resolved.map(item => _nodePath.default.normalize(item)) : _nodePath.default.normalize(/** @type {string} */resolved); const dependenciesOfDependencies = []; const items = /** @type {string[]} */ isArray ? result.resolved : [result.resolved]; for (const dependency of items) { // Avoid loop, the file is imported by itself if (seen.has(dependency)) { return; } // Avoid search nested imports in .css if (_nodePath.default.extname(dependency) === ".css") { return; } loaderContext.addDependency(dependency); dependenciesOfDependencies.push((async () => { let dependencyCode; try { dependencyCode = (await readFile(loaderContext.fs, dependency)).toString(); } catch (error) { loaderContext.emitError(/** @type {Error} */error); } await getDependencies(resolvedDependencies, loaderContext, fileResolver, globResolver, seen, /** @type {string} */dependencyCode, dependency, options); })()); } await Promise.all(dependenciesOfDependencies); })); if (dependencies.length > 0) { resolvedDependencies.set(filename, /** @type {Dependency[]} */ /** @type {unknown} */dependencies); } } /** * @param {import("stylus").nodes.Block[]} blocks blocks * @returns {import("stylus").nodes.Block | undefined} merged block */ function mergeBlocks(blocks) { /** @type {import("stylus").nodes.Block | undefined} */ let finalBlock; for (const block of blocks) { if (finalBlock) { for (const node of block.nodes) { finalBlock.push(node); } } else { finalBlock = block; } } return finalBlock; } /** * @param {LoaderContext} loaderContext loader context * @param {string} code code * @param {StylusOptions} options stylus options * @returns {Promise<typeof StylusEvaluator>} custom evaluator class */ async function createEvaluator(loaderContext, code, options) { const fileResolve = loaderContext.getResolve({ dependencyType: "stylus", conditionNames: ["styl", "stylus", "style", "..."], mainFields: ["styl", "style", "stylus", "main", "..."], mainFiles: ["index", "..."], extensions: [".styl", ".css"], restrictions: [/\.(css|styl)$/i], preferRelative: true }); // Get cwd for glob resolution // No need extra options, because they do not used when `resolveToContext` is `true` const globResolve = loaderContext.getResolve({ conditionNames: ["styl", "stylus", "style", "..."], resolveToContext: true, preferRelative: true }); /** @type {Map<string, Dependency>} */ const resolvedImportDependencies = new Map(); /** @type {Map<string, Dependency[]>} */ const resolvedDependencies = new Map(); /** @type {Set<string>} */ const seen = new Set(); await getDependencies(resolvedDependencies, loaderContext, fileResolve, globResolve, seen, code, loaderContext.resourcePath, options); /** @type {{ importPath: string, resolved: string | string[] | Promise<string | string[]>, error?: Error }[]} */ const optionsImports = []; for (const importPath of options.imports || []) { const isGlob = (0, _tinyglobby.isDynamicPattern)(importPath); optionsImports.push({ importPath, resolved: resolveFilename(loaderContext, fileResolve, globResolve, isGlob, _nodePath.default.dirname(loaderContext.resourcePath), importPath) }); } await Promise.all(optionsImports.map(async result => { const { importPath } = result; let { resolved } = result; try { resolved = await resolved; } catch { return; } const isArray = Array.isArray(resolved); // `stylus` returns forward slashes on windows result.resolved = isArray ? /** @type {string[]} */resolved.map(item => _nodePath.default.normalize(item)) : _nodePath.default.normalize(/** @type {string} */resolved); resolvedImportDependencies.set(importPath, /** @type {Dependency} */ /** @type {unknown} */result); const dependenciesOfImportDependencies = []; const items = /** @type {string[]} */ isArray ? result.resolved : [result.resolved]; for (const dependency of items) { dependenciesOfImportDependencies.push((async () => { let dependencyCode; try { dependencyCode = (await readFile(loaderContext.fs, dependency)).toString(); } catch (error) { loaderContext.emitError(/** @type {Error} */error); } await getDependencies(resolvedDependencies, loaderContext, fileResolve, globResolve, seen, /** @type {string} */dependencyCode, dependency, options); })()); } await Promise.all(dependenciesOfImportDependencies); })); return class CustomEvaluator extends Evaluator { /** * @param {import("stylus").nodes.Import} imported imported * @returns {import("stylus").nodes.Block | import("stylus").nodes.Import | undefined} visit result */ visitImport(imported) { const self = this; // @ts-expect-error internal logic self.return += 1; // @ts-expect-error no types are shipped for this const node = self.visit(imported.path).first; const nodePath = !node.val.isNull && node.val || node.name; // @ts-expect-error internal logic self.return -= 1; /** @type {Error & { details?: string, missing?: string[] } | undefined} */ let webpackResolveError; if (node.name !== "url" && nodePath && !URL_RE.test(nodePath)) { /** @type {Dependency | undefined} */ let dependency; let { filename } = node; if (_nodePath.default.sep === "\\") { filename = filename.replace(/^\/\/\?\//, ""); } const isEntrypoint = loaderContext.resourcePath === filename; if (isEntrypoint) { dependency = resolvedImportDependencies.get(nodePath); } if (!dependency) { const dependencies = resolvedDependencies.get(_nodePath.default.normalize(filename)); if (dependencies) { dependency = dependencies.find(item => { if (item.originalLineno === node.lineno && item.originalColumn === node.column && item.originalNodePath === nodePath) { if (item.error) { webpackResolveError = item.error; } else { return Boolean(item.resolved); } } return false; }); } } if (dependency) { const { resolved } = dependency; if (!Array.isArray(resolved)) { // Avoid re globbing when resolved import contains glob characters node.string = (0, _tinyglobby.escapePath)(/** @type {string} */resolved); } else if (resolved.length > 0) { let hasError = false; /** @type {import("stylus").nodes.Block[]} */ const blocks = resolved.map(item => { const clonedImported = imported.clone(); // @ts-expect-error no types are shipped for this const clonedNode = self.visit(clonedImported.path).first; // Avoid re globbing when resolved import contains glob characters clonedNode.string = (0, _tinyglobby.escapePath)(item); let result; try { // @ts-expect-error no types are shipped for this result = super.visitImport(clonedImported); } catch { hasError = true; } return result; }); if (!hasError) { return mergeBlocks(blocks); } } } } /** @type {import("stylus").nodes.Block | undefined} */ let result; try { // @ts-expect-error no types are shipped for this result = super.visitImport(imported); } catch (error) { loaderContext.emitError(new Error(`Stylus resolver error: ${ /** @type {Error} */error.message}${webpackResolveError ? `\n\nWebpack resolver error: ${webpackResolveError.message}${webpackResolveError.details ? `\n\nWebpack resolver error details:\n${webpackResolveError.details}` : ""}${webpackResolveError.missing ? `\n\nWebpack resolver error missing:\n${webpackResolveError.missing.join("\n")}` : ""}` : ""}`)); return imported; } return result; } }; } /** * @param {{ nocheck?: boolean, paths?: string[] } | boolean=} options url resolver options * @returns {EXPECTED_ANY} url resolver function */ function urlResolver(options = {}) { /** @type {{ nocheck?: boolean, paths?: string[] }} */ const resolverOptions = typeof options === "boolean" ? {} : options; /** * @this {import("stylus").Evaluator & { paths: string[], filename: string, includeCSS?: boolean }} * @param {import("stylus").nodes.Expression} url url node * @returns {import("stylus").nodes.Literal} a Literal node */ function resolver(url) { // @ts-expect-error no types are shipped for this const compiler = new Compiler(url); let { filename } = url; if (_nodePath.default.sep === "\\") { filename = filename.replace(/^\/\/\?\//, ""); } // @ts-expect-error no types are shipped for this compiler.isURL = true; // @ts-expect-error no types are shipped for this const visitedUrl = url.nodes.map(node => compiler.visit(node)).join(""); const splitted = visitedUrl.split("!"); const parsedUrl = (0, _nodeUrl.parse)(/** @type {string} */splitted.pop()); // Parse literal const literal = new nodes.Literal(`url("${parsedUrl.href}")`); let { pathname } = parsedUrl; // @ts-expect-error no types are shipped for this let { dest } = this.options; let tail = ""; let res; // Absolute or hash if (parsedUrl.protocol || !pathname || pathname[0] === "/") { return literal; } // Check that file exists if (!resolverOptions.nocheck) { const _paths = resolverOptions.paths || []; // @ts-expect-error bad types pathname = utils.lookup(pathname, [..._paths, ...(_nodePath.default.sep === "\\" ? this.paths.map((/** @type {string} */item) => _nodePath.default.normalize(item.replace(/^\/\/\?\//, ""))) : this.paths)]); if (_nodePath.default.sep === "\\" && pathname) { pathname = pathname.replace(/^\\\\\?\\/, ""); } if (!pathname) { return literal; } } if (this.includeCSS && _nodePath.default.extname(pathname) === ".css") { return new nodes.Literal(parsedUrl.href); } if (parsedUrl.search) { tail += parsedUrl.search; } if (parsedUrl.hash) { tail += parsedUrl.hash; } if (dest && _nodePath.default.extname(dest) === ".css") { dest = _nodePath.default.dirname(dest); } res = _nodePath.default.relative(dest || _nodePath.default.dirname(this.filename), resolverOptions.nocheck ? _nodePath.default.join(_nodePath.default.dirname(filename), pathname) : pathname) + tail; if (_nodePath.default.sep === "\\") { res = (0, _normalizePath.default)(res); } splitted.push(res); return new nodes.Literal(`url("${splitted.join("!")}")`); } resolver.options = resolverOptions; resolver.raw = true; return resolver; } const IS_NATIVE_WIN32_PATH = /^[a-z]:[/\\]|^\\\\/i; const ABSOLUTE_SCHEME = /^[A-Za-z0-9+\-.]+:/; /** * @param {string} source source * @returns {"absolute" | "scheme-relative" | "path-absolute" | "path-relative"} a type of URL */ function getURLType(source) { if (source[0] === "/") { if (source[1] === "/") { return "scheme-relative"; } return "path-absolute"; } if (IS_NATIVE_WIN32_PATH.test(source)) { return "path-absolute"; } return ABSOLUTE_SCHEME.test(source) ? "absolute" : "path-relative"; } /** * @param {RawSourceMap} map source map * @param {string} rootContext root context * @returns {RawSourceMap} normalized source map */ function normalizeSourceMap(map, rootContext) { const newMap = map; // result.map.file is an optional property that provides the output filename. // Since we don't know the final filename in the webpack build chain yet, it makes no sense to have it. delete newMap.file; newMap.sourceRoot = ""; newMap.sources = newMap.sources.map(source => { if (_nodePath.default.sep === "\\") { source = _nodePath.default.normalize(source.replace(/^\/\/\?\//, "")); } const sourceType = getURLType(source); // Do no touch `scheme-relative`, `path-absolute` and `absolute` types if (sourceType === "path-relative") { return _nodePath.default.resolve(rootContext, _nodePath.default.normalize(source)); } return source; }); return newMap; }