Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The fastest way to Transpile TypeScript in Node

What is the best(realtime?) way to transpile Typescript for node?

I'm using WebStorm and gulp with task backend:watch running in the background that listens to the changes. So when I hit "save" in WebStorm it does transpiles TS into JS and stores under /build directory.

My approach works well but transpiling is time consumable, - each run takes two or three seconds, seconds become minutes, and so on.

Is there a way to optimize it, a better alternative?

  • https://www.npmjs.com/package/ts-node is an alternative, but I'm uncertain that its better than what I currently have.
  • Also, heard good about new VisualStudio based on Electron but it saves JS files in the same location, that looks untidy to me.

    //////////////////////////////////////////////
    // Backend tasks
    //////////////////////////////////////////////
    
    const appSourceDir = path.join(dir, '/app');
    const appSourceGlob = `${appSourceDir}/**/*.*`;
    const appSourceRelativeGlob = 'app/**/*.*';
    const appCodeGlob = `${appSourceDir}/**/*.ts`;
    const appCodeRelativeGlob = 'app/**/*.ts';
    const appFilesGlob = [appSourceGlob, `!${appCodeGlob}`];
    const appFilesRelativeGlob = [appSourceRelativeGlob, `!${appCodeRelativeGlob}`];
    const appBuildDir = path.join(dir, '/build');
    
    
    gulp.task('backend:symlink', [], function (done) {
      const appTargetDir = path.join(dir, '/node_modules/app');
      // symlink for app
      fs.exists(appTargetDir, function (err) {
        if (!err) {
          fs.symlinkSync(appBuildDir, appTargetDir, 'dir');
        }
      });
    
      done();
    });
    
    gulp.task('backend:clean', function (done) {
      clean([appBuildDir + '/**/*', '!.gitkeep'], done);
    });
    
    gulp.task('backend:compile', function (done) {
      tsCompile([appCodeGlob], appBuildDir, appSourceDir, done);
    });
    
    gulp.task('backend:files', function () {
      // copy fixtures and other non ts files
      // from app directory to build directory
      return gulp
        .src(appFilesGlob)
        .pipe(plugin.cached('files'))
        .pipe(gulp.dest(appBuildDir));
    });
    
    gulp.task('backend:build', function (done) {
      sequence(
        'backend:clean',
        'backend:compile',
        'backend:files',
        done
      );
    });
    
    gulp.task('backend:watch:code', function () {
      const watcher = gulp.watch([appCodeRelativeGlob], ['backend:compile']);
    
      watcher.on('change', function (event) {
        // if a file is deleted, forget about it
        if (event.type === 'deleted') {
          // gulp-cached remove api
          delete plugin.cached.caches.code[event.path];
          delete plugin.event.caches.lint[event.path];
          // delete in build
          del(getPathFromSourceToBuild(event.path, appSourceDir, appBuildDir));
        }
      });
    });
    
    gulp.task('backend:watch:files', function () {
      const watcher = gulp.watch([appFilesRelativeGlob], ['backend:files']);
    
      watcher.on('change', function (event) {
        // if a file is deleted, forget about it
        if (event.type === 'deleted') {
          // gulp-cached remove api
          delete plugin.cached.caches.files[event.path];
          delete plugin.event.caches.lint[event.path];
          // delete in build
          del(getPathFromSourceToBuild(event.path, appSourceDir, appBuildDir));
        }
      });
    });
    
    gulp.task('backend:watch', ['backend:build'], function (done) {
      // first time build all by backend:build,
      // then compile/copy by changing
      gulp
        .start([
          'backend:watch:code',
          'backend:watch:files'
        ], done);
    });
    
    //////////////////////////////////////////////
    // Helpers
    //////////////////////////////////////////////
    
    /**
     * remaps file path from source directory to destination directory
     * @param {string} file path
     * @param {string} source directory path
     * @param {string} destination directory path
     * @returns {string} new file path (remapped)
     */
    function getPathFromSourceToBuild(file, source, destination) {
      // Simulating the {base: 'src'} used with gulp.src in the scripts task
      const filePathFromSrc = path.relative(path.resolve(source), file);
      // Concatenating the 'destination' absolute
      // path used by gulp.dest in the scripts task
      return path.resolve(destination, filePathFromSrc);
    }
    
    /**
     * @param  {Array}    path - array of paths to compile
     * @param  {string}   dest - destination path for compiled js
     * @param  {string}   baseDir - base directory for files compiling
     * @param  {Function} done - callback when complete
     */
    function tsCompile(path, dest, baseDir, done) {
      const ts = plugin.typescript;
      const tsProject = ts.createProject('tsconfig.json');
    
      gulp
        .src(path, {base: baseDir})
        // used for incremental builds
        .pipe(plugin.cached('code'))
        .pipe(plugin.sourcemaps.init())
        .pipe(tsProject(ts.reporter.defaultReporter())).js
        .pipe(plugin.sourcemaps.write('.'))
        .pipe(gulp.dest(dest))
        .on('error', done)
        .on('end', done);
    }
    
    /**
     * Delete all files in a given path
     * @param  {Array}   path - array of paths to delete
     * @param  {Function} done - callback when complete
     */
    function clean(path, done) {
      log('Cleaning: ' + plugin.util.colors.blue(path));
      del(path).then(function (paths) {
        done();
      });
    }
    
    /**
     * Log a message or series of messages using chalk's blue color.
     * Can pass in a string, object or array.
     */
    function log(msg) {
      if (typeof (msg) === 'object') {
        for (let item in msg) {
          if (msg.hasOwnProperty(item)) {
            plugin.util.log(plugin.util.colors.blue(msg[item]));
          }
        }
      } else {
        plugin.util.log(plugin.util.colors.blue(msg));
      }
    }

Also published on GitHub, see https://github.com/ivanproskuryakov/loopplate-node.js-boilerplate

like image 229
Ivan Proskuryakov Avatar asked May 09 '18 19:05

Ivan Proskuryakov


People also ask

How do I make my TS node faster?

Skip typechecking​ It is often better to typecheck as part of your tests or linting. You can use tsc --noEmit to do this. In these cases, ts-node can skip typechecking making it much faster.

How do I speed up TypeScript compilation?

The first thing that we can consider doing to improve performance, is to skip type checking between other files by setting the isolatedModules compiler option to true . This brings the average compile time down to 9.09 seconds , a 32% reduction in time (not bad for a quick setting change).

How do I initialize a TypeScript project?

With TypeScript installed, you can initialize your TypeScript project by using the following command: npx tsc --init.


2 Answers

Did you try just tsc --watch without gulp nor npm in the middle ? That is what I've found the fastest way of watch and compile my project. It takes 1 - 2 seconds the first time - but then is almost instantaneous. Get rid of all technologies you can if your objective is to be as fast as possible - even npm will take half a second - I guess gulp even more.

Also, in an other side, if you are working with multiple typescript projects, make sure you are using the new TypeScript feature Composite projects, https://www.typescriptlang.org/docs/handbook/project-references.html - in my case I'm working with a mono-repo and several projects need to be compiled and this feature improves speed a lot and simplify the compilation workflow.

Take in mind that TypeScript is more than a compiler, it's also a Language Service - that's why -watch will do the job better than just tsc - it will perform partial compilation

like image 68
cancerbero Avatar answered Oct 21 '22 10:10

cancerbero


Update on my question - I've got fast results with fewer lines with simply by switching to `ts-node as below.

{
  ...
  "scripts": {
    "start": "ts-node server.ts",
    "dev": "ts-node-dev --respawn --transpileOnly server.ts",
    "test": "./node_modules/.bin/mocha --compilers ts:ts-node/register ./test/**/**/**/*.ts",
  },
  ...
}

package.json contents, more https://github.com/ivanproskuryakov/express-typescript-boilerplate

like image 1
Ivan Proskuryakov Avatar answered Oct 21 '22 11:10

Ivan Proskuryakov