Gulp sourcemaps with TypeScript and Babel
Asked Answered
P

1

7

I am currently writing a side project where I can learn more about TypeScript and ES6 (using babel).

I wanted to use ES6 with my TypeScript, so I settled on the following workflow.

Typescript (ES6) -> Babel (ES6) -> ES5

Now I am using Gulp to automate all of this, and I am having a hard time getting the sourcemaps to generate properly. I should mention that this style was suggested to me by a user on /r/typescript so I am not even sure if it is possible.

Anyways here is my current gulp task

var server = $.typescript.createProject('src/server/tsconfig.json');
gulp.task('build', ['vet'], function () {
  var sourceRoot = path.join(__dirname, 'src/server/**/*.ts');
  return gulp.src('src/server/**/*.ts')
    .pipe($.sourcemaps.init())
      .pipe($.typescript(server))
      .pipe($.babel())
    .pipe($.sourcemaps.write('.', { sourceRoot: sourceRoot}))
    .pipe(gulp.dest('build/server'));
});

I have tried many different approaches, but none work. When debugging in VSCode, the entrypoint of my app: build/server/index.js loads and finds the source file src/server/index.ts properly.

However when VSCode attempts to step into another file say build/server/utils/logger/index.js it looks for src/server/utils/logger/index.js which it doesn't find because it should be looking for a *.ts file.

So what am I doing wrong? Or is this even possible? I've been staring at this for about 5 hours now. So any insight would be great.

Also VSCode 0.9.x displays the '.../.js' file not found and VSCode 1.0 just fails silently.

my tsconfig.json, it gets passed in by $.typescript.createProject()

{
  "compilerOptions": {
    "module": "commonjs",
    "noImplicitAny": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "target": "ES6",
    "sourceMap": true,
    "outDir": "build/server"
  }
}

.babelrc

{
  "presets": ["es2015"]
}

Here is the relevant npm packages

"devDependencies": {
    "babel-polyfill": "^6.2.0",
    "babel-preset-es2015": "^6.1.18",
    "gulp-babel": "^6.1.0",
    "gulp-sourcemaps": "^1.6.0",
    "gulp-typescript": "^2.9.2"
}

Edit: I have done some investigating into the gulp-sourcemaps, and when not using babel the sourcemaps work properly. (Removed all irrelevant info)

src/server/up/up2/four.ts - No Babel

{
  "history": [ "/home/user/code/test/src/server/up/up2/four.js" ],
  "base": "/home/user/code/test/src/server/",
  "sourceMap": {
    "sources": ["up/up2/four.ts"],
    "file": "up/up2/four.js"
  }
}

Notice how in sourceMap.sources it lists the proper source file up/up2/four.ts

Now here is an example when I add gulp-babel into the task.

src/server/up/up2/four.ts - With Babel

{
  "history": [ "/home/user/code/test/src/server/up/up2/four.js" ],
  "base": "/home/user/code/test/src/server/",
  "sourceMap": {
    "sources": ["four.js"],
    "file": "up/up2/four.js"
  },
  "babel": {
    "...": "..."
  }
}

Notice how the sourceMap.sources now incorrectly shows the four.js instead of the typescript file.

Curiously enough, as long as the typescript files are in the root directory src/server they build the source maps just fine.

src/server/two.ts - With Babel

{
  "history": [ "/home/user/code/test/src/server/two.js" ],
  "base": "/home/user/code/test/src/server/",
  "sourceMap": {
    "sources": ["two.ts"],
    "file": "two.js"
  },
  "babel": {
    "...": "..."
  }
}
Portfire answered 20/11, 2015 at 2:9 Comment(5)
Could you edit the question with your TypeScript and Babel configuration?Val
I have added my tsconfig.json, relevant package.json, and .babelrc. This is my first experience with babel, and going off the docs this is what I came up with.Portfire
Thanks, please see my answer below. Any reason you don't want to just target ES5 with Typescript and skip Babel completely? I know feature support and spec compliance are worse in TS, but it's worked for fairly complicated projects in the past for me.Val
I guess I just wanted to try out ES6, its not necessary, but I thought it would be fun to learn.Portfire
Typescript is a superset of ES6. You can compile it to pure ES6 (basically, just drop the types) and then use Babel to compile to ES5, as you are doing now. Or you can compile it directly to ES5 (skip the intermediate step). The latter will let you skip a build step, at the cost of less compliant ES5 output.Val
V
3

Update

It appears that the specific issue in this question is related to Babel's incorrect source map generation for files which are not in the working directory. There is already an issue filed here.

For a vinyl File object like

new File({
  cwd: '.',
  base: './test/',
  path: './test/some/file.js'
  ...
});

the generated source map should have something like

{
  ...
  "sources": ["some/file.js"]
  ...
}

but gulp-babel gives

{
  ...
  "sources": ["file.js"]
  ...
}

This causes the Typescript source maps and Babel source maps to be incorrectly merged, but only when the file is deeper than the working directory.

While this issue is being resolved, I recommend targeting ES5 with Typescript and bypassing Babel completely. This produces correct source maps.

gulp.task('build', function () {
  return gulp.src('src/server/**/*.ts')
    .pipe(sourcemaps.init())
    .pipe(typescript({
      noImplicitAny: true,
      removeComments: true,
      preserveConstEnums: true,
      target: 'es5',
      module: 'commonjs'
    }))
    .pipe(sourcemaps.write('.', { sourceRoot: 'src/server' }))
    .pipe(gulp.dest('build/server'));
});

Previous Answer

You are close, but there are a couple mistakes I noticed in your configuration:

  1. "module": "commonjs" is incompatible with "target": "es6". Output ES6 modules with Typescript and let Babel transpile them to CommonJS.
  2. "outDir" is not necessary when using Gulp since you are working with a stream. Intermediate results (i.e. results of Typescript) are not written to disk at all.
  3. "sourceMap": true is not necessary together with Gulp sourcemaps.

I have created a project which compiled for me, with [email protected] and [email protected].

Directory structure

.
├── gulpfile.js
└── src
    └── server
        ├── one.ts
        └── two.ts

one.ts

export class One {};

two.ts

import { One } from './one';

export class Two extends One {}

gulpfile.js

I have inlined all configurations for terseness, but you should be able to use config files just as easily.

var gulp = require('gulp');

var sourcemaps = require('gulp-sourcemaps');
var typescript = require('gulp-typescript');
var babel = require('gulp-babel');

gulp.task('build', function () {
  return gulp.src('src/server/**/*.ts')
    .pipe(sourcemaps.init())
    .pipe(typescript({
      noImplicitAny: true,
      removeComments: true,
      preserveConstEnums: true,
      target: 'es6'
    }))
    .pipe(babel({
      presets: [ 'es2015' ]
    }))
    .pipe(sourcemaps.write('.', { sourceRoot: 'src/server' }))
    .pipe(gulp.dest('build/server'));
});
Val answered 20/11, 2015 at 4:58 Comment(8)
I've setup a sample program like the one you provided. I never had a problem compiling the program. It was when I went to debug it using sourcemaps. I have done some more research and will add my findings to the first post.Portfire
How are you debugging your program? I have done a quick check using this module, and it seems that tracebacks are being mapped correctly using the generated source maps.Val
I finally finished my edit, took longer than expected. The problem with sourcemaps shows up when the source .ts file is in a subdirectory. The sourcemap ends up pointing to the wrong file, ie './one.ts' works, but sourcemap for 'up/two.ts' doesn't, see above. And I am using VSCode's built-in node debugger.Portfire
Interesting, I also was able to reproduce the issue. This may be relevant.Val
I've looked further into it. It appears that gulp-babel does not follow these instructions, specifically the second bullet point regarding file.relative. They always use the basename. The source map they generate cannot be applied on top of the existing TypeScript one. I will put together a runnable example and file and issue in the next day or two.Val
Thank you for your continued support into this problem, I really appreciate it.Portfire
Turns out there is an issue already filed. I have edited my answer. You can temporarily drop Babel and use the ES5 output and source maps generated by Typescript. They seem to be correct.Val
have already dropped the ES6 target as I was having other problems with Babel and NPM packages. I figure its best I learn ES6 in a different project. Thanks for all the help.Portfire

© 2022 - 2024 — McMap. All rights reserved.