SyntaxError: 'import' and 'export' may appear only with 'sourceType: module' in Gulp + Babel + TypeScript + Source Maps

Remo H. Jansen picture Remo H. Jansen · Nov 15, 2015 · Viewed 8.1k times · Source

I'm trying to compile from .ts to .min.js as follows:

TS --> ES6 ---> ES5 ---> .min.js + .map

Before I was just doing the following and everything was working fine:

TS ---> ES5 --->  .min.js + .map

I want to be able to use source maps. My tsconfig.json is the following:

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "isolatedModules": false,
    "jsx": "react",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "declaration": false,
    "noImplicitAny": false,
    "removeComments": true,
    "noLib": false,
    "preserveConstEnums": true,
    "suppressImplicitAnyIndexErrors": true
  }
}

Since I added "target": "es6" I'm getting the error:

SyntaxError: 'import' and 'export' may appear only with 'sourceType: module'

The tsify documentation says:

This error occurs when a TypeScript file is not compiled to JavaScript before being run through the Browserify bundler. There are a couple known reasons you might run into this.

But in my Gulp tasks in running tsify before babelify:

gulp.task("bundle", function() {

  var mainTsFilePath = "src/main.ts";
  var outputFolder   = "bundle/src/";
  var outputFileName = settings.projectName + ".min.js";
  var pkg            = require("./package.json");

  var banner = [
    "/**",
    " * <%= pkg.name %> v.<%= pkg.version %> - <%= pkg.description %>",
    " * Copyright (c) 2015 <%= pkg.author %>",
    " * <%= pkg.license %>",
    " */", ""
  ].join("\n");

  var bundler = browserify({
    debug: true,
    standalone : settings.projectName
  });

  var babelifyConfig = { extensions: ['.js','.jsx','.ts','.tsx'] };

  // TS compiler options are in tsconfig.json file
  return bundler.plugin(tsify)
                // Added this line and target es6
                .transform(babelify.configure(babelifyConfig)) 
                .add(mainTsFilePath)
                .bundle()
                .pipe(source(outputFileName))
                .pipe(buffer())
                .pipe(sourcemaps.init({ loadMaps: true }))
                .pipe(uglify())
                .pipe(header(banner, { pkg : pkg } ))
                .pipe(sourcemaps.write("."))
                .pipe(gulp.dest(outputFolder));
});

I just added the ES6 compilation, before I was compiling TS into ES5 and everything worked fine (including source maps).

I don't know what is wrong. Do you have any ideas? Thanks in advance!

Answer

Balthazar picture Balthazar · May 30, 2017

It appears there was an issue with browserify and its resolving of symlinked transforms. However, since this commit that make use of the fs.realpathSync method which returns the canonicalized absolute pathname, the problem should be fixed. And it has been definitely landed in the 10.2.5 version of node-browserify.

As mentioned by James, if you can't update to a newer version, you can checkout his answer using realpathify, which fixes the same issue.

How gulp and browerify work together is rather interesting, since it's one of the rare tools to actually don't need an associated gulp plugin (which has even been blacklisted at some point to force users to use the browserify package directly). The reason why it is not needed is due to the fact @substack, notably known for being the creator of browserify and tape, is pretty knowledgable (and I assume, likes) the usage of node-streams that he's using in these two projects. Given this premise, and the fact that 99% of gulp plugins are only plugging the tool they want to use with streams, a gulp plugin is unnecessary since browserify is already using streams you can return in your task.

Little side-note, he also wrote an handbook about doing streams in node that is worth checking out if you want to understand more about how gulp works internally.

Regarding tsify, it works the same way than any other plugin, compiling typescript files and passing the output to the stream for the browserify process to continue.