Skip to content

Instantly share code, notes, and snippets.

@devversion
Last active October 30, 2021 14:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devversion/549d25915c2dc98a8896ba4408a1e27c to your computer and use it in GitHub Desktop.
Save devversion/549d25915c2dc98a8896ba4408a1e27c to your computer and use it in GitHub Desktop.
Source Map Karma warning due to generated helpers by ESBuild runtime

As of v13, we bundle our specs with ESBuild to an AMD bundle so that the ESM output can continue to work with Karma in the browser. (There are more reasons on why this is done; not relevant here)

This means that for our development experience we need to heavily rely on source maps. e.g. for assertion errors. Without source maps it would be extremely difficult backtracking where an error ocurred as everything is in a single large AMD bundle file.

Errors initially look like this without source mappings:

Expected false to be true.
<Jasmine>
runHarnessTests/</<@http://localhost:9877/base/angular_material/src/material/select/testing/unit_tests_bundle_spec.js:72191:40
fulfilled@http://localhost:9877/base/angular_material/src/material/select/testing/unit_tests_bundle_spec.js:26:26
invoke@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:372:26
ProxyZoneSpec.prototype.onInvoke@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-testing.js:301:43
invoke@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:371:52
run@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:134:43
scheduleResolveOrReject/<@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:1276:36
invokeTask@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:406:31
ProxyZoneSpec.prototype.onInvokeTask@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-testing.js:332:43
invokeTask@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:405:60
runTask@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:178:47
drainMicroTaskQueue@http://localhost:9877/base/npm/node_modules/zone.js/dist/zone-evergreen.js:582:35

Karma processes the error using a custom reporter where it looks for files in the trace, seqentually visiting every file mentioned in the trace. It first comes across:

  • <Jasmine>
  • angular_material/src/material/select/testing/unit_tests_bundle_spec.js:72191:40
  • and so on.

If it discovers an actual file when iterating through the trace, it will load the file and its source map, trying to determine the original file + original position. All of this is reasonable so far.

There is one little issue though. Since we emit ES2020 output but downlevel to ES2016 for tests and the dev-app, async/await is transformed by ESBuild to use generators + a __async helper.

This helper function now also shows up in the stacktrace but has no original mapping as it is purely generated by ESBuild. Karma generates a warning for this (unexecptedly IMO), since it came across a file in the trace which could not be mapped. The problem is that the generated helper is part of a file which has a corresponding source-map.

SourceMap position not found for trace: http://localhost:9877/base/angular_material/src/material/select/testing/unit_tests_bundle_spec.js:26:26 

Still, the resolved stacktrace looks like this after the transformation:

	Expected false to be true.
	<Jasmine>
	runHarnessTests/</<@../../src/material/select/testing/shared.spec.ts:123:38 <- angular_material/src/material/select/testing/unit_tests_bundle_spec.js:72191:40
	fulfilled@angular_material/src/material/select/testing/unit_tests_bundle_spec.js:26:26
	invoke@npm/node_modules/zone.js/dist/zone-evergreen.js:372:26
	ProxyZoneSpec.prototype.onInvoke@npm/node_modules/zone.js/dist/zone-testing.js:301:43
	invoke@npm/node_modules/zone.js/dist/zone-evergreen.js:371:52
	run@npm/node_modules/zone.js/dist/zone-evergreen.js:134:43
	scheduleResolveOrReject/<@npm/node_modules/zone.js/dist/zone-evergreen.js:1276:36
	invokeTask@npm/node_modules/zone.js/dist/zone-evergreen.js:406:31
	ProxyZoneSpec.prototype.onInvokeTask@npm/node_modules/zone.js/dist/zone-testing.js:332:43
	invokeTask@npm/node_modules/zone.js/dist/zone-evergreen.js:405:60
	runTask@npm/node_modules/zone.js/dist/zone-evergreen.js:178:47
	drainMicroTaskQueue@npm/node_modules/zone.js/dist/zone-evergreen.js:582:35

The unnecessary warnings by Karma in the case of downleveled async/await is polluting the Karma test output currently, so it's something we should try to hide. It's unclear yet how t

Why did this not surface before or in angular/angular?

This does not surface there because we always build TypeScript with importHelpers: true, so that the __async helper will actually come from node_modules/tslib which has proper source mappings then/or no mappings at all (so there is no warning). ESBuild does not have such functionality.

Why does this not surface within the CLI, using ES2020 as well?

The CLI seems to rely on Babel here to downlevel async/await. The rest remains ES2020.

Simply using the downlevel async/await Babel plugin is not sufficient though as it results in the same problem of generated functions. The CLI solves this though by relying on similar helpers to tslib, called the @babel/runtime, so that the async generator wrapper is actually no longer part of a file for which source maps are not available (hence no attemp/warning):

Chrome 95.0.4638.69 (Mac OS 10.15.7) a b FAILED
        Error: Expected true to be false.
            at <Jasmine>
            at UserContext.<anonymous> (src/app/test.spec.ts:4:22)
            at Generator.next (<anonymous>)
            at asyncGeneratorStep (node_modules/@babel/runtime/helpers/asyncToGenerator.js

Potential solution

It looks like we can fix this by following what the CLI internally does. This would allow us to also run the tests with ES2020, while we'd just donwlevel the necessary async/await portion. We'd need to setup the babel runtime transform as well, so that the async helper is coming from an external file in "node_modules/@babel/runtime" (solving the source map warning issue from Karma since the helper comes from a file without any source maps)

	Expected false to be true.
	<Jasmine>
	runHarnessTests/<@../../src/material/select/testing/shared.spec.ts:123:38 <- angular_material/src/material/select/testing/unit_tests_bundle_spec.js:73855:40
	asyncGeneratorStep@../../node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:3:20 <- angular_material/src/material/select/testing/unit_tests_bundle_spec.js:72432:26
	_next@../../node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:25:9 <- angular_material/src/material/select/testing/unit_tests_bundle_spec.js:72450:29
	invoke@npm/node_modules/zone.js/dist/zone-evergreen.js:372:26
	ProxyZoneSpec.prototype.onInvoke@npm/node_modules/zone.js/dist/zone-testing.js:301:43
	invoke@npm/node_modules/zone.js/dist/zone-evergreen.js:371:52
	run@npm/node_modules/zone.js/dist/zone-evergreen.js:134:43
	scheduleResolveOrReject/<@npm/node_modules/zone.js/dist/zone-evergreen.js:1276:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment