4

I'm working on a Typescript project that is transpiled to ES5 JS and then run through browserify to create a single .js bundle and sourcemap. The bundled sourcemaps point to the transpiled JS rather than the source TS even though I am generating sourcemaps which properly point to the source TS when transpiling to JS.

It's as if browserify is ignoring the existing sourcemaps pointing to the TS code and creating its own new maps to the transpiled JS code.

Here are my gulp tasks for reference- code is compiled to a temp folder and then browserified from there. This uses browserify-incremental to support incremental compilation.

Thanks!

Note: Via other SO questions I have already tried using tsify, per my understanding it won't work with my project as we use import syntax & commonjs, it reports compile issues where tsc and gulp-typescript do not (same errors whether used via gulp or via CLI). I also tried minifyify but it did not solve the issue.

var gulp        = require('gulp'),
  ts          = require('gulp-typescript'),
  browserify = require('browserify'),
  browserifyInc = require('browserify-incremental'),
  source      = require('vinyl-source-stream'),
  del         = require('del'),
  sourcemaps  = require('gulp-sourcemaps'),
  buffer = require('vinyl-buffer'),
  xtend = require('xtend');

var tsProject = ts.createProject('tsconfig.json');

//COMPILE TS
gulp.task('compileTs', function () {
  var sourceTsFiles = [config.src.tsFiles, config.src.tsTypes];

  var tsResult = gulp.src(sourceTsFiles)
    .pipe(sourcemaps.init())
    .pipe(ts(tsProject));

  return tsResult.js
    .pipe(sourcemaps.write('.', {
      //includeContent: false,
      //sourceRoot: "../../src/js/"
    }))
    .pipe(gulp.dest(config.tempDir));
});

//BUNDLE BUILT TS->JS
gulp.task('bundleJs', [], function () {
  var b = browserify(xtend(browserifyInc.args, {
    entries: config.tempDir + '/main.js',
    extensions: ['.js', '.jsx'],
    debug: true
  }));

  browserifyInc(b,  {cacheFile: config.tempDir + '/browserify-cache.json'});

  return b.bundle()
  .pipe(source('main.js'))
  .pipe(buffer())
  .pipe(sourcemaps.init({ loadMaps: true }))
  .pipe(sourcemaps.write('.'))
  .pipe(gulp.dest(config.dest.jsDir));
});
danosphere
  • 41
  • 1
  • 6
  • Not sure of your specific tools, but most tools that write source maps will also take existing source maps in and write a new source map directing to the original files. Just a question of whether your tool supports it and how. – nbering Nov 24 '15 at 22:14
  • Possible duplicate of [Keep original typescript source maps after using browserify](http://stackoverflow.com/questions/23453160/keep-original-typescript-source-maps-after-using-browserify) – mixel Jul 31 '16 at 00:25
  • May be my answer http://stackoverflow.com/a/38679678/746347 will be helpful for someone. – mixel Jul 31 '16 at 00:37

2 Answers2

5

Short answer

In the compileTs task you need to write the sourcemaps to the output .js files, instead of dedicated .map files. You also need to set includeContent to true, and specify the correct sourceRoot.

Then in the bundleJs task, having browserify debug true is enough to generate sourcemaps.

More details

Some package doesn't provide the necessary source data to the sourcemaps utility in the bundle task. Luckily sourcemaps can re-read the .ts file. For that recovery step to work, it needs correct file paths, so that's why the correct sourceRoot in the TypeScript compilation task is so crucial.

There seem to be other gotchas here as well. For example if you write the sourcemaps to a dedicated .map file in the TypeScript compilation task, then later the bundle task will generate sourcemaps that point to the compiled .js files. So it's again crucial that the compilation task embeds the sourcemaps into the actual .js files.

If the debug flag for browserify is true, it will generate sourcemaps. The extra gulp-sourcemaps layering here has a buggy interaction and should be removed.

A working example from real life

Directory structure

proj
    /ts
        /def
            my-declarations.d.ts
        /src
            my-sources.ts
        /tmp
            temp-files-get-created-here-by-gulp.js
        tsconfig.json
    /web
        /static
            /js
                final-result-goes-here.js
    gulpfile.js
    package.json

tsconfig.json

{
    "compilerOptions": {
        "noImplicitAny": true,
        "removeComments": true,
        "declaration": false,
        "jsx": "React",
        "target": "ES5",
        "module": "CommonJS"
    },
    "exclude": [
        "tmp"
    ]
}

package.json

{
  "name": "my-awesome-project",
  "version": "0.1.0",
  "devDependencies": {
    "browserify": "^13.0.1",
    "gulp": "^3.9.1",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-typescript": "^2.13.6",
    "gulp-uglify": "^1.5.3",
    "gulp-util": "^3.0.7",
    "vinyl-buffer": "^1.0.0",
    "vinyl-source-stream": "^1.1.0"
  }
}

gulpfile.js

var path = require('path');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var ts = require('gulp-typescript');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');

var debug = false;

var paths = {
    tsConfig: 'ts/tsconfig.json',
    scriptsSrc: ['ts/def/**/*.ts', 'ts/src/**/*.ts', 'ts/src/**/*.tsx'],
    scriptsDst: 'web/static/js',
    outDev: 'bundle.dev.js',
    outFinal: 'bundle.js',
    tempDst: 'ts/tmp',
    entry: 'ts/tmp/entry.js'
};

var tsProject = ts.createProject(paths.tsConfig, { noExternalResolve: true });

gulp.task('ts', function () {
    var tsResult = tsProject.src().pipe(sourcemaps.init()).pipe(ts(tsProject));
    return tsResult.js.pipe(sourcemaps.write('', { debug: debug, includeContent: true, sourceRoot: './ts/src' })).pipe(gulp.dest(paths.tempDst));
});

gulp.task('dev', ['ts'], function() {
    var bsfy = browserify({ entries: paths.entry, debug: true }); // Debug true generates sourcemaps
    return bsfy.bundle()
        .on('error', gutil.log)
        .pipe(source(path.join(paths.scriptsDst, paths.outDev)))
        .pipe(buffer())
        .pipe(gulp.dest('./'));
});

gulp.task('final', ['ts'], function() {
    process.env.NODE_ENV = 'production';
    var bsfy = browserify({ entries: paths.entry, debug: false });
    return bsfy.bundle()
        .on('error', gutil.log)
        .pipe(source(path.join(paths.scriptsDst, paths.outFinal)))
        .pipe(buffer())
        .pipe(uglify())
        .pipe(gulp.dest('./'));
});

// Rerun the dev task when a file changes
gulp.task('watch', function() {
    gulp.watch(paths.scriptsSrc, ['dev']);
});

// By default run all the tasks
gulp.task('default', ['dev', 'final']);
Strom
  • 356
  • 2
  • 6
-1

If you use both tasks at the same time, the second task will write new source maps. I'd suggest, that you only write source maps once, in the compileTs task.

I think this is the only problem in your tasks.

FlorianTopf
  • 938
  • 6
  • 10