diff --git a/lib/SourceMapDevToolPlugin.js b/lib/SourceMapDevToolPlugin.js index fc5a3dcf287..49ebabc9b30 100644 --- a/lib/SourceMapDevToolPlugin.js +++ b/lib/SourceMapDevToolPlugin.js @@ -47,13 +47,32 @@ const validate = createSchemaValidation( * @property {ItemCacheFacade} cacheItem cache item */ +const METACHARACTERS_REGEXP = /[-[\]\\/{}()*+?.^$|]/g; +const CONTENT_HASH_DETECT_REGEXP = /\[contenthash(:\w+)?\]/; +const CSS_AND_JS_MODULE_EXTENSIONS_REGEXP = /\.((c|m)?js|css)($|\?)/i; +const CSS_EXTENSION_DETECT_REGEXP = /\.css($|\?)/i; +const MAP_URL_COMMENT_REGEXP = /\[map\]/g; +const URL_COMMENT_REGEXP = /\[url\]/g; +const URL_FORMATTING_REGEXP = /^\n\/\/(.*)$/; + +/** + * Reset's .lastIndex of stateful Regular Expressions + * For when `test` or `exec` is called on them + * @param {RegExp} regexp Stateful Regular Expression to be reset + * @returns {void} + * + */ +const resetRegexpState = regexp => { + regexp.lastIndex = -1; +}; + /** * Escapes regular expression metacharacters * @param {string} str String to quote * @returns {string} Escaped string */ const quoteMeta = str => { - return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&"); + return str.replace(METACHARACTERS_REGEXP, "\\$&"); }; /** @@ -152,7 +171,7 @@ class SourceMapDevToolPlugin { const fallbackModuleFilenameTemplate = this.fallbackModuleFilenameTemplate; const requestShortener = compiler.requestShortener; const options = this.options; - options.test = options.test || /\.((c|m)?js|css)($|\?)/i; + options.test = options.test || CSS_AND_JS_MODULE_EXTENSIONS_REGEXP; const matchObject = ModuleFilenameHelpers.matchObject.bind( undefined, @@ -409,7 +428,9 @@ class SourceMapDevToolPlugin { sourceMap.file = file; const usesContentHash = sourceMapFilename && - /\[contenthash(:\w+)?\]/.test(sourceMapFilename); + CONTENT_HASH_DETECT_REGEXP.test(sourceMapFilename); + + resetRegexpState(CONTENT_HASH_DETECT_REGEXP); // If SourceMap and asset uses contenthash, avoid a circular dependency by hiding hash in `file` if (usesContentHash && task.assetInfo.contenthash) { @@ -428,13 +449,16 @@ class SourceMapDevToolPlugin { /** @type {string | false} */ let currentSourceMappingURLComment = sourceMappingURLComment; + let cssExtensionDetected = + CSS_EXTENSION_DETECT_REGEXP.test(file); + resetRegexpState(CSS_EXTENSION_DETECT_REGEXP); if ( currentSourceMappingURLComment !== false && - /\.css($|\?)/i.test(file) + cssExtensionDetected ) { currentSourceMappingURLComment = currentSourceMappingURLComment.replace( - /^\n\/\/(.*)$/, + URL_FORMATTING_REGEXP, "\n/*$1*/" ); } @@ -516,9 +540,9 @@ class SourceMapDevToolPlugin { const asset = new ConcatSource( new RawSource(source), currentSourceMappingURLComment - .replace(/\[map\]/g, () => sourceMapString) + .replace(MAP_URL_COMMENT_REGEXP, () => sourceMapString) .replace( - /\[url\]/g, + URL_COMMENT_REGEXP, () => `data:application/json;charset=utf-8;base64,${Buffer.from( sourceMapString,