Bugsnag-js: Uploading source maps for Typescript files

Created on 27 Jun 2018  Â·  10Comments  Â·  Source: bugsnag/bugsnag-js

Hi.

I'm trying to upload the source maps of a NodeJS project written in Typescript to Bugsnag. The code doesn't get minified, uglyfied or concatenated. Just compiled from Typescript to Javascript. The project is running on Firebase Functions.

First of all is this even possible to upload the source maps for Typescript to be shown on the dashboard?

Steps to reproduce

What I tried is using the bugsnag-sourcemaps package to upload the source maps manually. I also triggered the following error: Preview

Currently I'm just trying to upload the single source map file in which the error is thrown

const upload = require('bugsnag-sourcemaps').upload;
const glob = require('glob');
const bugsnagKey = '...';
const path = require('path');

const projectRoot = path.resolve(__dirname, '..');
const dist = path.resolve(projectRoot, 'dist');

glob(`${dist}/app/controllers/internal_controller.js.map`, (err, files) => {
    if (err) throw err;

    Promise.all(files.map(processMap))
        .then((result) => {
            console.log(result);
        })
        .catch((err) => {
            console.error(err);
            process.exit(1);
        });
});

function processMap(sourceMapFile) {
    const localFile = sourceMapFile.split('.map')[0];
    console.log('Local file:', localFile);
    const relativeFile = localFile.split(dist)[1];
    const remoteFileUrl = `/user_code/dist${relativeFile}`;
    console.log('URL:', remoteFileUrl);
    const tsFile = path.join(projectRoot, 'src', relativeFile.replace('.js', '.ts'));
    console.log('TS file:', tsFile);
    return upload({
        apiKey: bugsnagKey,
        // appVersion: appVersion,
        minifiedUrl: remoteFileUrl,
        sourceMap: sourceMapFile,
        minifiedFile: localFile,
        projectRoot: projectRoot,
        sources: {
            [remoteFileUrl]: tsFile
        },
        uploadSources: true,
        overwrite: true,
    });
};

Upload is a success and those lines get printed:

Local file: /home/dominic/workspace/tablechamp-api/dist/app/controllers/internal_controller.js
URL: /user_code/dist/app/controllers/internal_controller.js
TS file: /home/dominic/workspace/tablechamp-api/src/app/controllers/internal_controller.ts

Version

Using "bugsnag-sourcemaps": "^1.0.3",

Additional information

I'm not setting any project version while uploading. The error in the dashboard doesn't show a version as well.

Project: freshfox/tablechamp-api

Most helpful comment

It shouldn't be too hard, you'll just want to glob over all .map files in your directory and call upload() on them, along with the appopriate parameters for bugsnag-sourcemaps.

const glob = require('glob')
const fs = require('fs')
const parallel = require('run-parallel-limit')
const { upload } = require('bugsnag-sourcemaps')

// find every .map file in your dist directory
glob('path/to/dist/**/*.map', (err, files) => {
  if (err) throw err

  // for each source map, get an upload function and
  // pass it to the parallel runner utility – this limits the
  // upload concurrency to 5 at once
  parallel(files.map(f => getUploadFn(f), 5, (err) => {
    if (err) throw err
    console.log('done')
  })
})

function getUploadFn (f) {
  return () => {
    console.log(`Uploading source map ${f}`)
    upload({
      apiKey: 'YOUR_API_KEY_HERE',
      appVersion: '1.2.3',
      // this must match the path that will appear in your error reports
      minifiedUrl: path.relative(projectRoot, f.replace(/\.map$/, '')),
      // this is the path to the source map
      sourceMap: f,
      // this is the path to the js file that the source map is for
      minifiedFile: f.replace(/\.map$/, '')
    })
  }
}

Note that this is just a sketch – I just wrote it directly here on GitHub and didn't run it!

All 10 comments

Hi @Bartinger. At this point in time we don't support source maps for Node projects – just browser JavaScript and React Native. We're currently working on a project that will add this for Node and it should be supported in the next couple of months. Stay tuned and thanks for your patience!

Any update on this Node project, @bengourley? I'm having trouble reporting errors in my ionic projects.

It's well underway on the universal branch of this repo if you want to be nosey! But it's not quite ready for release yet.

Any updates on this? We find that when running ts-node in production, bugsnag correctly reports the line numbers in the typescript source, but there's a performance overhead to that approach and we'd like to be able to compile to plain JS and send sourcemaps to bugsnag.

To clarify, I know the universal API was released, but the bugsnag upload API still feels like it's written with front-end JS expectations in mind. A node app isn't minified, nor would tsc generate a single JS bundle or a single sourcemap. Maybe I'm getting myself twisted in knots over this, but it seems like what I'll need to do is write a custom script that will recursively dive down through my src directory and call the upload API for every single generated .js/.map.js combo which seems like a pain.

bugsnag upload API still feels like it's written with front-end JS expectations in mind

This is absolutely the case! It does however work for Node, React Native etc., it's just that some of the parameters have front-end specific names. We're looking at updating this with more appropriate names at some point.

it seems like what I'll need to do is write a custom script that will recursively dive down through my src directory and call the upload API for every single generated .js/.map.js combo which seems like a pain.

That's exactly what needs to happen. We have a project scheduled to add this feature to the bugsnag-sourcemaps tool so that you don't have to do that yourself.

I'm sure you can understand why we wouldn't have wanted to hold back on releasing universal js until we had the chance to make these tooling updates, so please bear with us while we get on to that work. Thanks for your patience!

Maybe I'm getting myself twisted in knots over this, but it seems like what I'll need to do is write a custom script that will recursively dive down through my src directory and call the upload API for every single generated .js/.map.js combo which seems like a pain.

@bmakuh - If you ever end up writing that, it'd be great if you could consider open sourcing it 😄. I'd have done it myself, but it's not really a priority for me at the moment.

It shouldn't be too hard, you'll just want to glob over all .map files in your directory and call upload() on them, along with the appopriate parameters for bugsnag-sourcemaps.

const glob = require('glob')
const fs = require('fs')
const parallel = require('run-parallel-limit')
const { upload } = require('bugsnag-sourcemaps')

// find every .map file in your dist directory
glob('path/to/dist/**/*.map', (err, files) => {
  if (err) throw err

  // for each source map, get an upload function and
  // pass it to the parallel runner utility – this limits the
  // upload concurrency to 5 at once
  parallel(files.map(f => getUploadFn(f), 5, (err) => {
    if (err) throw err
    console.log('done')
  })
})

function getUploadFn (f) {
  return () => {
    console.log(`Uploading source map ${f}`)
    upload({
      apiKey: 'YOUR_API_KEY_HERE',
      appVersion: '1.2.3',
      // this must match the path that will appear in your error reports
      minifiedUrl: path.relative(projectRoot, f.replace(/\.map$/, '')),
      // this is the path to the source map
      sourceMap: f,
      // this is the path to the js file that the source map is for
      minifiedFile: f.replace(/\.map$/, '')
    })
  }
}

Note that this is just a sketch – I just wrote it directly here on GitHub and didn't run it!

Thanks for the great explanation!

Okay, took some pointers from @bengourley - Here's a gulp version if it helps someone. Uses Bluebird and Globby.

gulp.task("compile", function compile() {
  return tsProject
    .src()
    .pipe(sourcemaps.init())
    .pipe(tsProject())
    .js.pipe(
      sourcemaps.write(".", { includeContent: false, sourceRoot: "../src" })
    )
    .pipe(gulp.dest("dist"))
})

function getUploadFn(mapPath) {
  const jsPath = path.normalize(mapPath.replace(/\.map$/, ""))
  const tsPath = path.normalize(mapPath.replace(/\js\.map$/, "ts"))
  return upload({
    apiKey: config.BUGSNAG_API_KEY,
    // this must match the path that will appear in your error reports
    minifiedUrl: jsPath,
    // this is the path to the js file that the source map is for
    minifiedFile: tsPath,
    sourceMap: mapPath,
    overwrite: true
  }).then(() => {
    console.log(`Uploaded ${mapPath}`)
  })
}

gulp.task("bugsnag", async () => {
  const paths = await globby("./dist/**/*.js.map")
  await Bluebird.map(paths, getUploadFn, { concurrency: 15 })
  console.log(`Sourcemap upload complete for ${process.env.NODE_ENV}`)
})
Was this page helpful?
0 / 5 - 0 ratings

Related issues

v3solutions picture v3solutions  Â·  6Comments

antoinerousseau picture antoinerousseau  Â·  4Comments

rafmst picture rafmst  Â·  3Comments

mattimatti picture mattimatti  Â·  4Comments

killia15 picture killia15  Â·  6Comments