question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proper source map support for development mode

See original GitHub issue

Hello. In order to speed up our development process, we have build a custom solution for the hypernova server which can load Javascript bundles over HTTP. With that, we can spin up a webpack dev server and reload the compiled bundle each time the source files change. That has been working wonders for the time being and we are really happy that we don’t have to compile the files over and over again.

In order to make development even better, we wanted to add source map support for the generated stack traces. Our current implementation is the following.

  1. Client asks for component Foo
  2. We load the code for component Foo over the webpack dev server
  3. We evaluate its code with vm.runInNewContext and we catch any errors generated
  4. Should we catch any error, we load the source map file and we feed it into a sourceMapConsumer from Mozilla’s source-map.
  5. We then map all the stack trace elements into new ones by querying the consumer.
  6. Finally we throw the modified error.

Code would look something like this

let scriptContent;
  try {
    const response = await axios.get(url);
    scriptContent = response.data;
  } catch (e) {
    console.error('Error while getting remote file');
    console.error(e);
    throw e;
  }
  let module = require('module');

  const wrappedCode = module.wrap(scriptContent);
  const moduleRunner = vm.runInNewContext(wrappedCode, createContext(), {
    displayErrors: true,
    filename: 'ssr.node.js'
  });

  const { data } = await axios.get(`${url}.map`)
  const sourceMapConsumer = await new sourceMap.SourceMapConsumer(data);

  return () => {
    const modExports = {};
    const modContext = {};

    try {
      moduleRunner(modExports, require, modContext, __filename, __dirname);
    } catch(error) {
      // Get the trace lines
      const trace = error.stack.split("\n").slice(1).map(l => l.trim());
      const originalStackTrace = sourceMappedStackTrace(trace, sourceMapConsumer);
      // Map them to the original files
      // Construct the new stack trace
      const newTrace = originalStackTrace.map((orig, index) => {
        if(orig.source === null || orig.line === null || orig.column === null) {
          // Something that we don't have a mapping for. Leave the original
          return `    ${trace[index]}`;
        }
        let base = '    at ';
        if(orig.name) {
          base += `${orig.name} `;
        }
        base += `${orig.source}:${orig.line}:${orig.column}`;
        return base;
      })
      .filter(e => e !== null)

      // Throw the modified error
      throw new Error(error.stack.split('\n')[0] + '\n' + newTrace.join('\n'))
    }
    return modContext.exports;

Although this works great for the time being, we wanted to catch “runtime” errors as well. That is, errors generated when (e.g. ReactDOM.renderToString runs). To my understanding, there is no way to manipulate such errors from BatchManager.render as the exceptions are caught and modified there.

We could submit a patch upstream to enable custom error handling if there is no other elegant way of manipulating error traces.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:2
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
agiscommented, Nov 21, 2018

@ljharb For example, the backtrace displayed on the browser is something along the lines of:

"ReferenceError: idontexist is not defined",
     "at new SampleComponent (ssr.bundle.js:35348:5)",
     "at processChild (ssr.bundle.js:34431:14)",
     "at resolve (ssr.bundle.js:34397:5)",
     "at ReactDOMServerRenderer.render (ssr.bundle.js:34724:22)",
     "at ReactDOMServerRenderer.read (ssr.bundle.js:34695:25)",
     "at Object.renderToString (ssr.bundle.js:35109:25)",
     "at ssr.bundle.js:11566:46",
     "at /home/dev/node_modules/hypernova/lib/utils/BatchManager.js:190:18",
     "at tryCatcher (/home/dev/node_modules/bluebird/js/release/util.js:16:23)",
     "at Promise._settlePromiseFromHandler (/home/dev/node_modules/bluebird/js/release/promise.js:512:31)"

Note that:

  • we compile our SSR code using webpack DevServer into a single bundle and that bundle (ssr.bundle.js) is loaded by hypernova
  • the above backtrace is displayed in the Rails view, since we have the hypernova developer plugin enabled
  • what we’re trying to achieve, is to have the trace point to the line of the original source file/lineno (before it’s been compiled by webpack), which would be something like app/assets/javascript/foo.js:3

Could it be that rolling our own implementation (with vm.runInNewContext()) instead of using createGetComponent is messing up the source maps?

Thanks!

1reaction
ljharbcommented, Nov 21, 2018

Gotcha. So you do have a source map for that bundle, you just want it somehow applied in the hypernova error.

That does seem like something that could be done inside hypernova itself. However, perhaps if you included https://www.npmjs.com/package/source-map-support yourself, it would be handled for you?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Source Maps - SurviveJS
Source maps can be helpful both during development and production. They provide information about what's going on and speed up debugging. Webpack supports...
Read more >
How to set up source maps to help debug production JavaScript
These five tips help developers get source maps working in JavaScript so they can spend less time debugging and more time writing software....
Read more >
Should I Use Source Maps in Production? | CSS-Tricks
Typically, source maps are a configuration option from the preprocessor. Here's Babel's options. I believe that with Sass, you don't even have ...
Read more >
What Are Source Maps and How to Properly Use Them
The developer tools for both Chrome and Firefox have built in support for source maps making it even easier to trace the compressed...
Read more >
JavaScript Debugging with Sourcemaps - TrackJS
Both Chrome and Firefox currently support the processing of sourcemaps in the developer tools. Additionally, logging & monitoring tools like TrackJS are ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found