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

3

11

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!

Cilo answered 15/11, 2015 at 11:17 Comment(4)
Is there a specific reason why you first generate ES6 with typescript and then to ES5 with babelify? How is your babelifyConfig looking?Samellasameness
The plan is to let babel take care of the polyfills because it does that job better than TypeScript, so I get the string typing benefits from TS and the polyfills benefits from babel. My babelify config is only setting some extensions: var babelifyConfig = { extensions: ['.js','.jsx','.ts','.tsx'] };Cilo
@RemoH.Jansen Did you solve this?Vickievicksburg
Solved my own problem. I will award the bounty for a good explanation of how gulp, tsify, and babelify work together.Vickievicksburg
G
2

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.

Goodkin answered 15/11, 2015 at 11:17 Comment(5)
You basically pointed to our answers, copypasted one paragraph, added a list of their separate description and a basic explanation of what gulp is (totally out of scope). Try to be more creative next time.Relict
@Aperçu If you found any plagiarism feel free to flag my answer. The bounty poster said an in depth explanation of Gulp, TSify, and Babelify and how they work together would be appropriate for the bounty, and that's what I did. Also, for the interest of future readers, please undelete your answer, it's really valuable.Goodkin
No, he absolutely never asked for an explanation of what they are separately, please reread. You also have to understand that even though there is a bounty, you have to try staying on topic with the initial OP. Besides, I don't think you really find it valuable, otherwise you would have upvoted it, right?Relict
@Aperçu You're right; I guess I was blinded by the bounty. I'll go ahead and put a flag in to make my answer community wiki. I'd highly recommend undeleting your answer here because it actually answers the question unlike mine as you mentioned.Goodkin
I don't think it will change anything, but thanks for acknowledging.Relict
R
2

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.

Relict answered 30/5, 2017 at 1:0 Comment(0)
V
0

I had the same problem, which was caused by having symlinks in my node_modules. I fixed it by adding the realpathify plugin to browserify.

var gulp = require("gulp")
var browserify = require("browserify")
var babelify = require("babelify")
var source = require('vinyl-source-stream')
var tsify = require("tsify")
var sourcemaps = require('gulp-sourcemaps')
var buffer = require('vinyl-buffer');
var realpathify = require('realpathify')

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

    browserify({
        basedir: '.',
        debug: true,
        cache: {},
        packageCache: {},
        entries: [ 'app/browser.ts' ]
    })
    .plugin(tsify)
    .plugin(realpathify)
    .transform(babelify, {extensions: ['.js', '.ts'], presets: ['es2015']})
    .bundle()
    .on('error', function (error) { report(error) })
    .pipe(source('bundle.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(sourcemaps.write('./'))
    .pipe(gulp.dest("dist"))
})
Vickievicksburg answered 29/5, 2017 at 22:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.