7

I am currently working on TypeScript. I want to replace JS with TS, but I have a lot of JS-Files, so that I only want to create new classes in TS and want to use these in my old JS files atm. Later on I want to replace all JS with TS.

I not 100% familiar with webpack and bundled js files, so that I could use some help.

Is there any opportunity to compile TS into JS with gulp (already done - it works) AND use these TS classes in different (old) JS files?

TypeScript file:

class Car {
    seats: number;
    passengers: string[];

    add(passenger: string) {
        if(this.passengers.indexOf(passenger) === -1) {
            this.passengers.push(passenger);
        }
    }
}

JavaScript file:

var car = new Car();
car.seats = 5;
car.add("Pete");

As you can mention, Car is undefined.

How can I use my Car class in other JS files? (after ts compilation and webpack bundling)

This is my old gulp task:

gulp.task('bundle-ts-webpack', ['compile-ts-webpack'], function() {
    var tsFiles = glob.sync(config.js.tsSource);

    return webpackStream({
        /* configuration options */
        entry: tsFiles,
        module: {
            rules: [
                {
                    //test: /\.tsx?$/,
                    use: 'ts-loader',
                    exclude: /node_modules/
                }
            ]
        },
        resolve: {
            extensions: ['.tsx', '.ts', '.js'],
            // use ts in js - begin
            alias: {
                'TS': path.resolve(config.js.tsDest, 'ts-bundle'),
                'ExpertFilterHelper': path.resolve('./static/ts/', 'car'),
                'test': path.resolve('.static/ts/', 'test-globals')
            }
            // use ts in js - end
        },
        // use ts in js - begin
        plugins: [
            new webpack.ProvidePlugin({
                'TS': 'TS',
                identifier: 'car',
                test: 'test'
            })
        ],
        // use ts in js - end
        output: {
            filename: 'ts-bundle.js',
            path: path.resolve(config.js.tsDest, 'dist')
        }
    })
        .pipe(gulp.dest(config.js.tsDest));
});

I already tried to get this running as you can see in the gulp.task, but I still "dont know what I am doing here".

Can someone give me a hint?

I already thought, whether I should compile TS to JS first and then bundle it via webpack, but I dont know whether this solve my problem.

Edit/Updated:

As Joshua Barnett mentioned, you can use the expose-loader, but I still have a question to the expose-loader:

Can I only add one typescript file to the globals?

I have three typescript files for exmaple:

  • A.ts
  • B.ts
  • Car.ts

This works fine: I can now use:

Library.Car()

BUT: When I have another typescript file/class like so:

  • A.ts
  • B.ts
  • Car.ts
  • Delivery.ts

I can only use Library.Delivery() and cannot use Library.Car() anymore. Any hints for that?

My current gulp.task:

gulp.task('compile-ts-webpack', function() {
    var tsFiles = glob.sync('./static/ts/*.ts');

    console.log(require.resolve('./static/ts/z-car-test.ts'));

    return webpackStream(
        {
            entry: tsFiles,//path.resolve('./static/ts/z-car-test.ts'),
            output: {
                filename: 'ts-bundle.js',
                path: path.resolve('./static/js/modules')
            },
            devtool: "source-map",
            resolve: {
                extensions: ['.tsx', '.ts', '.js']
            },
            module: {
                rules: [{
                    test: /\.ts|\.tsx$/, //require.resolve('./static/ts/z-car-test.ts'),
                    use: [{
                        loader: 'expose-loader',
                        options: 'TSGlobals'
                    }, {
                        loader: 'ts-loader'
                    }]
                    //exclude: /node_modules/
                }]
            }
        }
    ).pipe(gulp.dest('./static/js/modules'));

Edit/Update 2:

If you want to have more than one class added to the TSGlobals you can do that with a little hack (I guess it's a hack, im not sure)

I created a exporter.ts which re-exports all classes I need to be global to use these in any js-file as Joshua Barnett mentioned in a comment:

//exporter.ts
export { Car } from "./car.ts";
export { Delivery } from "./delivery.ts";

I also need to tell webpack to bundle the exporter typescript file at the end of the files-array. (Im not 100% sure why)

//gulp.task
gulp.task('compile-ts', ['clean-ts'], function() {
    var tsFiles = glob.sync('./ts/*.ts', {"ignore":['./exporter.ts']}); // Ignore exporter.ts

//add the exporter.ts to the end of the array to provide the global TSGlobals Library.
    tsFiles.push(config.js.tsExporterFile);
[...]
}

Now I can use TSGlobals in any JS-file! Works perfectly.

//whatever.js
var car = new TSGlobals.Car();
car.seats = 5;
car.add('Pete');

var delivery = new TSGlobals.Delivery();
delivery.doSomething("123");
Ismoh
  • 1,074
  • 2
  • 12
  • 35

1 Answers1

5

You need to make use of the expose-loader in order to make your module available through the global scope.


// File: webpack.config.js
const path = require('path')

module.exports = {
  context: path.resolve('src'),
  entry: './main.ts',
  module: {
    rules: [
      {
        test: require.resolve('./src/main.ts'),
        use: [{
          loader: 'expose-loader',
          options: 'Library'
        }, {
          loader: 'ts-loader'
        }],
        exclude: /node_modules/
      }
    ]
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
}

// File: main.ts
export class Car {
    seats: number;
    passengers: string[];

    add(passenger: string) {
        if(this.passengers.indexOf(passenger) === -1) {
            this.passengers.push(passenger);
        }
    }
}

// File: main.js
var car = new Library.Car()
car.seats = 5
car.add('Pete')

Make sure the code calling the module is included after the module bundle.

<!-- File: index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Webpack App</title>
  </head>
  <body>
    <script type="text/javascript" src="bundle.js"></script>
    <script src="main.js"></script>
  </body>
</html>
James W.
  • 154
  • 3
  • 13
jshbrntt
  • 5,134
  • 6
  • 31
  • 60
  • This looks pretty nice, but I still have a question: I need to pass each ts-file I want to have in the "Library", like so: `test: require.resolve('./src/main.ts'), require.resolve('./src/another.ts')`. Is that correct? – Ismoh Nov 27 '17 at 08:08
  • It seems that I only pass one ts file to globals. I updated my question. – Ismoh Nov 27 '17 at 09:25
  • 1
    You need to have a TypeScript module which exports all of the classes you care about then use that to export more than one class. So write all your classes in different files then combine the ones you need to expose in a module in another file. Then reference the module file in the Webpack configuration file. Hope that helps. – jshbrntt Nov 27 '17 at 10:57
  • Yes that helped alot, thank you. But there is still the problem, that the ts module, which exports all of the classes, has to be at the bottom of the directory tree, as you can see at the updated section in the question. Any ideas to prevent this module to be passed at the end? – Ismoh Nov 27 '17 at 11:44
  • If I change test from `test: /\.ts|\.tsx$/` to: `test: require.resolve('./src/main.ts')` the gulp task failed, because: `You may need an appropriate loader to handle this file type.` – Ismoh Nov 27 '17 at 11:53
  • Works, see Edit/Update 2. Thank you very much @Joshua Barnett – Ismoh Nov 27 '17 at 15:50