Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!

like image 493
Remo H. Jansen Avatar asked Nov 15 '15 11:11

Remo H. Jansen


1 Answers

The reason why you get the error is described well in Aperçu's answer. An update should fix the issue as noted, but you can see James' answer if you are unable to.

Gulp, Babelify, and TSify all work together to transpile your code from TypeScript ES2015 to a browser-compatible pure ES5 with modules. Here's a basic introduction to what they are:

  • Gulp - A task runner that uses streams to allow for you to group and execute certain smaller tasks at once for efficiency and simplicity in builds

  • Babelify - A Browserify transformer that transforms your files in place to pure JavaScript before bundling, compatible with the browser depending on your presets and plugins; Babel for Browserify

  • TSify - A Browserify plugin that compiles your TypeScript to JavaScript for the browser

Using Gulp, you can setup both Browserify plugins to convert your TypeScript files into ECMAScript 2015 files in one easy task. Instead of using gulp-browserify which is blacklisted, you can go ahead and just use the browserify package because it already uses streams, which is what Gulp expects so there's no need for an extra Gulp plugin.

Now onto how they work together. Think of Gulp as a factory that makes apple pies, and your Gulp tasks as certain tasks your factory performs so that it can create the final product: making the dough, creating the filling, baking the pie, etc. Say I wanted to create the filling, I need to start by:

  • Picking some apples and importing them
  • Transforming the apples with lemon juice for flavor
  • Heating then adding all ingredients
  • Letting it simmer and cool till it's ready for the next phase

These are like the certain parts of the task in your Gulp task. If I wanted to create browser runnable JavaScript from TypeScript, I would similarly:

  • Pick my target files to be transpiled
  • Transform my target files with a plugin (TSify) and compile them to (in this case) ES2015
  • Transform my target files with a transformer (Babelify) to transpile them from TSify's ES2015 to ES5 for the browser

Applying this to actual code we get this:

  1. Create a new Gulp task to transpile our code from TypeScript ES2015 to pure ES5
  2. Use browserify instance and streams to select entry files where the code will be transpiled, then pass them down the stream
  3. Continue with the files in the stream by registering a plugin, tsify, that will run and convert your TypeScript files in the stream to ES2015 (if the target is ES2015), then pass them down the stream
  4. Hand off the new ES2015 files in the stream to babelify which will transform the ES2015 to browser-friendly ES5 with certain preset es2015, then pass them down the stream for the pipeline to continue

All parts work in tandem to allow you to have one task that does multiple things, eventually converting your TypeScript to ES5.

like image 150
Andrew Li Avatar answered Sep 20 '22 00:09

Andrew Li