Cheap source maps don’t work with harmony modules (incompatible produced output)

Do you want to request a feature or report a bug?
Bug

What is the current behavior?
Harmony modules do not produce line-to-line source code, which breaks cheap sourcemaps.

If the current behavior is a bug, please provide the steps to reproduce.
bar.js

export const parent = 'bar'
export const test = 'ing'
export const parent2 = 'bar'
export const test2 = 'ing'
export const parent3 = 'bar'
export const test3 = 'ing'
export const parent4 = 'bar'
export const test4 = 'ing'

foo.js

export default 'bar'

index.js

import bar from './foo'
import * as bar2 from './foo'
import { parent } from './bar'
import { test } from './bar'
import { parent3 } from './bar'
import { test3 } from './bar'
import { parent2 } from './bar'
import { test2 } from './bar'

console.log(bar, bar2, parent, test)

What is the expected behavior?
Correct cheap sourcemaps.


source-list-map expects originalSource and generatedSource to be the same number of lines (which it is not).

Though probably not properly fixing it, updating ReplaceSource.js in webpack-sources and adding

newValue = newValue.replace(/\n/g, ';')

will strip newlines from inserted code, preventing line mismatches.

Alternatively, a patch like this also fixes it:

diff --git a/lib/dependencies/HarmonyCompatibilityDependency.js b/lib/dependencies/HarmonyCompatibilityDependency.js
index 016b6236..67342e4d 100644
--- a/lib/dependencies/HarmonyCompatibilityDependency.js
+++ b/lib/dependencies/HarmonyCompatibilityDependency.js
@@ -21,7 +21,7 @@ HarmonyCompatibilityDependency.Template = class HarmonyExportDependencyTemplate
 		const usedExports = dep.originModule.usedExports;
 		if(usedExports && !Array.isArray(usedExports)) {
 			const exportName = dep.originModule.exportsArgument || "exports";
-			const content = `Object.defineProperty(${exportName}, \"__esModule\", { value: true });\n`;
+			const content = `Object.defineProperty(${exportName}, \"__esModule\", { value: true });`;
 			source.insert(-1, content);
 		}
 	}
diff --git a/lib/dependencies/HarmonyExportImportedSpecifierDependency.js b/lib/dependencies/HarmonyExportImportedSpecifierDependency.js
index ba236e8a..b5bba004 100644
--- a/lib/dependencies/HarmonyExportImportedSpecifierDependency.js
+++ b/lib/dependencies/HarmonyExportImportedSpecifierDependency.js
@@ -174,12 +174,12 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
 
 		// we want to rexport something, but the export isn't used
 		if(!used) {
-			return "/* unused harmony reexport " + dep.name + " */\n";
+			return "/* unused harmony reexport " + dep.name + " */";
 		}
 
 		// we want to reexport something but another exports overrides this one
 		if(!active) {
-			return "/* inactive harmony reexport " + (dep.name || "namespace") + " */\n";
+			return "/* inactive harmony reexport " + (dep.name || "namespace") + " */";
 		}
 
 		// we want to reexport the default export from a non-hamory module
@@ -212,7 +212,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
 			}).filter(Boolean);
 
 			if(items.length === 0) {
-				return "/* unused harmony namespace reexport */\n";
+				return "/* unused harmony namespace reexport */";
 			}
 
 			return items.map(function(item) {
@@ -232,7 +232,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
 			}).filter(Boolean);
 
 			if(items.length === 0) {
-				return "/* empty harmony namespace reexport */\n";
+				return "/* empty harmony namespace reexport */";
 			}
 
 			return items.map(function(item) {
@@ -252,10 +252,10 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
 			else
 				content += "if(__WEBPACK_IMPORT_KEY__ !== 'default') ";
 			const exportsName = dep.originModule.exportsArgument || "exports";
-			return content + `(function(key) { __webpack_require__.d(${exportsName}, key, function() { return ${name}[key]; }) }(__WEBPACK_IMPORT_KEY__));\n`;
+			return content + `(function(key) { __webpack_require__.d(${exportsName}, key, function() { return ${name}[key]; }) }(__WEBPACK_IMPORT_KEY__));`;
 		}
 
-		return "/* unused harmony reexport namespace */\n";
+		return "/* unused harmony reexport namespace */";
 	}
 
 	reexportStatementCreator(module, importsExportsUnknown, name) {
@@ -263,7 +263,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
 		const getReexportStatement = (key, valueKey) => {
 			const conditional = this.getConditional(importsExportsUnknown, valueKey, name);
 			const returnValue = this.getReturnValue(valueKey);
-			return `${conditional}__webpack_require__.d(${exportsName}, ${key}, function() { return ${name}${returnValue}; });\n`;
+			return `${conditional}__webpack_require__.d(${exportsName}, ${key}, function() { return ${name}${returnValue}; });`;
 		};
 		return getReexportStatement;
 	}
diff --git a/lib/dependencies/HarmonyExportSpecifierDependency.js b/lib/dependencies/HarmonyExportSpecifierDependency.js
index 0fb5f828..b428a2cc 100644
--- a/lib/dependencies/HarmonyExportSpecifierDependency.js
+++ b/lib/dependencies/HarmonyExportSpecifierDependency.js
@@ -48,19 +48,19 @@ HarmonyExportSpecifierDependency.Template = class HarmonyExportSpecifierDependen
 		const used = dep.originModule.isUsed(dep.name);
 		const active = HarmonyModulesHelpers.isActive(dep.originModule, dep);
 		if(!used) {
-			return `/* unused harmony export ${(dep.name || "namespace")} */\n`;
+			return `/* unused harmony export ${(dep.name || "namespace")} */`;
 		}
 
 		if(!active) {
-			return `/* inactive harmony export ${(dep.name || "namespace")} */\n`;
+			return `/* inactive harmony export ${(dep.name || "namespace")} */`;
 		}
 
 		const exportsName = dep.originModule.exportsArgument || "exports";
 		if(dep.immutable) {
-			return `/* harmony export (immutable) */ ${exportsName}[${JSON.stringify(used)}] = ${dep.id};\n`;
+			return `/* harmony export (immutable) */ ${exportsName}[${JSON.stringify(used)}] = ${dep.id};`;
 		}
 
-		return `/* harmony export (binding) */ __webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${dep.id}; });\n`;
+		return `/* harmony export (binding) */ __webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${dep.id}; });`;
 	}
 };
 
diff --git a/lib/dependencies/HarmonyImportDependency.js b/lib/dependencies/HarmonyImportDependency.js
index 59f9b226..9975e61c 100644
--- a/lib/dependencies/HarmonyImportDependency.js
+++ b/lib/dependencies/HarmonyImportDependency.js
@@ -49,20 +49,19 @@ function getOptionalComment(pathinfo, shortenedRequest) {
 function makeImportStatement(declare, dep, outputOptions, requestShortener) {
 	const comment = getOptionalComment(outputOptions.pathinfo, requestShortener.shorten(dep.request));
 	const declaration = declare ? "var " : "";
-	const newline = declare ? "\n" : " ";
 
 	if(!dep.module) {
 		const stringifiedError = JSON.stringify(`Cannot find module "${dep.request}"`);
-		return `throw new Error(${stringifiedError});${newline}`;
+		return `throw new Error(${stringifiedError});`;
 	}
 
 	if(dep.importedVar) {
 		const isHarmonyModule = dep.module.meta && dep.module.meta.harmonyModule;
-		const content = `/* harmony import */ ${declaration}${dep.importedVar} = __webpack_require__(${comment}${JSON.stringify(dep.module.id)});${newline}`;
+		const content = `/* harmony import */ ${declaration}${dep.importedVar} = __webpack_require__(${comment}${JSON.stringify(dep.module.id)});`;
 		if(isHarmonyModule) {
 			return content;
 		}
-		return `${content}/* harmony import */ ${declaration}${dep.importedVar}_default = __webpack_require__.n(${dep.importedVar});${newline}`;
+		return `${content}/* harmony import */ ${declaration}${dep.importedVar}_default = __webpack_require__.n(${dep.importedVar});`;
 	}
 
 	return "";

I would love some guidance on fixing this properly — this aims to resolve #3579.

Imports can be more properly solved by not appending newlines for each import statement, though I can’t find where that happens, e.g.:

*snip* __WEBPACK_IMPORTED_MODULE_1__bar__ = __webpack_require__(0);\n\n\n\n\n\n\n\n\n

Removing these additional newlines will allow the import statements to have their own trailing newline.

@sokra some insight would be great. 😄 I’d love to fix this if not way too advanced.

Huge thanks to @TheLarkInn for debugging this with me.

Author: Fantashit

4 thoughts on “Cheap source maps don’t work with harmony modules (incompatible produced output)

  1. This is published as webpack-sources@0.2.0 but webpack doesn’t use this version yet. It would be great if you can verify if this version works fine. Override your webpack-sources version with 0.2.0.
    npm install webpack-sources@0.2.0.

    cc @gaearon

Comments are closed.