Skip to content

Commit d53432a

Browse files
committed
feature #50 Add support to enableTypescriptLoader (davidmpaz, weaverryan)
This PR was merged into the master branch. Discussion ---------- Add support to enableTypescriptLoader Hi, please would you consider adding this to main stream? this add public API to enable TypeScript loader ([ts-loader](https://github.com/TypeStrong/ts-loader)). It follows much of what is described in #9 Cuurrently only add vanilla loader as described in [here](https://github.com/TypeStrong/ts-loader/blob/master/examples/vanilla/webpack.config.js) playing side by side with babel. Future work would be to include also support to `happypack` and `fork-ts-checker-webpack-plugin` Thanks in advance Commits ------- 6d5dee8 Fixing bad merge 35d9e6f Merge branch 'master' into typescript 82e17e3 Remove unnecessary functional test 4d5319d Update yarn.lock with typescript deps 144acd2 Cleaning up unnecessary code. f6bc6e7 Rename to enableTypeScriptLoader facc132 Remove package-lock.json 25b9f18 Remove duplicated default configuration options 23619b2 Add test for error handling on missing ts loader d887a79 Add typescript to missing loader error handling 61370ac Add .ts & .tsx to resolved extension 6ec2b52 Revert "Add .idea directory to gitignore rules" 356aaa2 Make tests check for content in browser context 822ceff Improve tests for compiling .tsx files ae1bea3 Update packages-lock.json file a02d8ab Add .idea directory to gitignore rules 32c2853 Revert accidental version change 846d142 Update tests to take into account refactoring b43f5c9 Refactor to favor configure by callback 1352d1c Add missing extensions to config.resolve 7e8c118 Fix copy'n paste errors 58b7ef7 Add unit and functional test for TypeScript loader fc26e1b Add support to enable TypeScript loader
2 parents c5dd738 + 6d5dee8 commit d53432a

File tree

16 files changed

+246
-3
lines changed

16 files changed

+246
-3
lines changed

fixtures/js/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import render = require('./render');
2+
3+
render();

fixtures/js/render.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function render() {
2+
document.getElementById('app').innerHTML = "<h1>Welcome to Your TypeScript App</h1>";
3+
}
4+
5+
export = render;

fixtures/js/render2.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
function render() {
2+
document.getElementById('app').innerHTML = "<h1>Welcome to Your TypeScript App</h1>";
3+
}
4+
5+
export = render;

fixtures/js/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"compilerOptions": {}
3+
}

index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,23 @@ module.exports = {
342342
return this;
343343
},
344344

345+
/**
346+
* Call this if you plan on loading TypeScript files.
347+
*
348+
* Encore.enableTypeScriptLoader(function(tsConfig) {
349+
* // change the tsConfig
350+
* });
351+
*
352+
* Supported configuration options:
353+
* @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#available-options
354+
*
355+
* @param {function} callback
356+
* @return {exports}
357+
*/
358+
enableTypeScriptLoader(callback) {
359+
webpackConfig.enableTypeScriptLoader(callback);
360+
}
361+
345362
/**
346363
* If enabled, the Vue.js loader is enabled.
347364
*

lib/WebpackConfig.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class WebpackConfig {
5353
this.useVueLoader = false;
5454
this.vueLoaderOptionsCallback = () => {};
5555
this.loaders = [];
56+
this.useTypeScriptLoader = false;
57+
this.tsConfigurationCallback = function() {};
5658
}
5759

5860
getContext() {
@@ -224,6 +226,16 @@ class WebpackConfig {
224226
this.useReact = true;
225227
}
226228

229+
enableTypeScriptLoader(callback) {
230+
this.useTypeScriptLoader = true;
231+
232+
if (typeof callback !== 'function') {
233+
throw new Error('Argument 1 to enableTypeScriptLoader() must be a callback function.');
234+
}
235+
236+
this.tsConfigurationCallback = callback;
237+
}
238+
227239
enableVueLoader(vueLoaderOptionsCallback = () => {}) {
228240
this.useVueLoader = true;
229241

lib/config-generator.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const cssLoaderUtil = require('./loaders/css');
2929
const sassLoaderUtil = require('./loaders/sass');
3030
const lessLoaderUtil = require('./loaders/less');
3131
const babelLoaderUtil = require('./loaders/babel');
32+
const tsLoaderUtil = require('./loaders/typescript');
3233
const vueLoaderUtil = require('./loaders/vue');
3334

3435
class ConfigGenerator {
@@ -72,7 +73,7 @@ class ConfigGenerator {
7273
config.stats = this.buildStatsConfig();
7374

7475
config.resolve = {
75-
extensions: ['.js', '.jsx', '.vue'],
76+
extensions: ['.js', '.jsx', '.vue', '.ts', '.tsx'],
7677
alias: {}
7778
};
7879

@@ -161,6 +162,14 @@ class ConfigGenerator {
161162
});
162163
}
163164

165+
if (this.webpackConfig.useTypeScriptLoader) {
166+
this.webpackConfig.addLoader({
167+
test: /\.tsx?$/,
168+
exclude: /node_modules/,
169+
use: tsLoaderUtil.getLoaders(this.webpackConfig)
170+
});
171+
}
172+
164173
this.webpackConfig.loaders.forEach((loader) => {
165174
rules.push(loader);
166175
});

lib/friendly-errors/transformers/missing-loader.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ function transform(error) {
4949
case 'jsx':
5050
error.loaderName = 'react';
5151
break;
52+
case 'tsx':
53+
case 'ts':
54+
error.loaderName = 'typescript';
55+
break;
5256
// add more as needed
5357
default:
5458
return error;

lib/loader-features.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ const loaderFeatures = {
3636
packages: ['babel-preset-react'],
3737
description: 'process React JS files'
3838
},
39+
typescript: {
40+
method: 'enableTypeScriptLoader()',
41+
packages: ['typescript', 'ts-loader'],
42+
description: 'process TypeScript files'
43+
},
3944
vue: {
4045
method: 'enableVueLoader()',
4146
// vue is needed so the end-user can do things

lib/loaders/typescript.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const loaderFeatures = require('../loader-features');
13+
const babelLoader = require('./babel');
14+
15+
/**
16+
* @param {WebpackConfig} webpackConfig
17+
* @return {Array} of loaders to use for TypeScript
18+
*/
19+
module.exports = {
20+
getLoaders(webpackConfig) {
21+
loaderFeatures.ensureLoaderPackagesExist('typescript');
22+
23+
// some defaults
24+
let config = {
25+
silent: true,
26+
};
27+
28+
// allow for ts-loader config to be controlled
29+
webpackConfig.tsConfigurationCallback.apply(
30+
// use config as the this variable
31+
config,
32+
[config]
33+
);
34+
35+
// use ts alongside with babel
36+
// @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#babel
37+
let loaders = babelLoader.getLoaders(webpackConfig);
38+
return loaders.concat([
39+
{
40+
loader: 'ts-loader',
41+
// @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#available-options
42+
options: config
43+
}
44+
]);
45+
}
46+
};

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"loader-utils": "^1.1.0",
4040
"lodash": ">=3.5 <5",
4141
"pkg-up": "^1.0.0",
42-
"pretty-error": "^2.1.0",
42+
"pretty-error": "^2.1.1",
4343
"resolve-url-loader": "^2.0.2",
4444
"style-loader": "^0.13.2",
4545
"webpack": "^2.2.0",
@@ -64,6 +64,8 @@
6464
"postcss-loader": "^1.3.3",
6565
"sass-loader": "^6.0.3",
6666
"sinon": "^2.3.4",
67+
"ts-loader": "^2.1.0",
68+
"typescript": "^2.3.4",
6769
"vue": "^2.3.4",
6870
"vue-loader": "^12.2.1",
6971
"vue-template-compiler": "^2.3.4",

test/WebpackConfig.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,27 @@ describe('WebpackConfig object', () => {
278278
});
279279
});
280280

281+
describe('enableTypeScriptLoader', () => {
282+
it('Calling method sets it', () => {
283+
const config = createConfig();
284+
const testCallback = () => {};
285+
config.enableTypeScriptLoader(testCallback);
286+
expect(config.tsConfigurationCallback).to.equal(testCallback);
287+
});
288+
289+
it('Calling with non-callback throws an error', () => {
290+
const config = createConfig();
291+
292+
expect(() => {
293+
config.enableTypeScriptLoader('FOO');
294+
}).to.throw('must be a callback function');
295+
296+
expect(() => {
297+
config.enableTypeScriptLoader();
298+
}).to.throw('must be a callback function');
299+
});
300+
});
301+
281302
describe('addPlugin', () => {
282303
it('extends the current registered plugins', () => {
283304
const config = createConfig();

test/friendly-errors/transformers/missing-loader.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,5 +60,18 @@ describe('transform/missing-loader', () => {
6060
expect(actualError.type).to.deep.equal('loader-not-enabled');
6161
expect(actualError.loaderName).to.deep.equal('sass');
6262
});
63+
64+
it('Typescript error is properly transformed', () => {
65+
const startError = {
66+
name: 'ModuleParseError',
67+
message: 'You may need an appropriate loader',
68+
file: '/path/to/file.ts'
69+
};
70+
const actualError = transform(Object.assign({}, startError));
71+
72+
expect(actualError.name).to.deep.equal('Loader not enabled');
73+
expect(actualError.type).to.deep.equal('loader-not-enabled');
74+
expect(actualError.loaderName).to.deep.equal('typescript');
75+
});
6376
});
6477
});

test/functional.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe('Functional tests using webpack', function() {
4545
testSetup.emptyTmpDir();
4646
});
4747

48-
describe('Basic scenarios', () => {
48+
describe('Basic scenarios.', () => {
4949

5050
it('Builds a few simple entries file + manifest.json', (done) => {
5151
const config = createWebpackConfig('web/build', 'dev');
@@ -583,6 +583,40 @@ module.exports = {
583583
});
584584
});
585585

586+
it('When configured, TypeScript is compiled!', (done) => {
587+
const config = createWebpackConfig('www/build', 'dev');
588+
config.setPublicPath('/build');
589+
config.addEntry('main', ['./js/render.ts', './js/index.ts']);
590+
const testCallback = () => {};
591+
config.enableTypeScriptLoader(testCallback);
592+
593+
testSetup.runWebpack(config, (webpackAssert) => {
594+
// check that ts-loader transformed the ts file
595+
webpackAssert.assertOutputFileContains(
596+
'main.js',
597+
'document.getElementById(\'app\').innerHTML = "<h1>Welcome to Your TypeScript App</h1>";'
598+
);
599+
600+
expect(config.outputPath).to.be.a.directory().with.deep.files([
601+
'main.js',
602+
'manifest.json'
603+
]);
604+
605+
testSetup.requestTestPage(
606+
path.join(config.getContext(), 'www'),
607+
[
608+
'build/main.js'
609+
],
610+
(browser) => {
611+
612+
// assert that the ts module rendered
613+
browser.assert.text('#app h1', 'Welcome to Your TypeScript App');
614+
done();
615+
}
616+
);
617+
});
618+
});
619+
586620
it('The output directory is cleaned between builds', (done) => {
587621
const config = createWebpackConfig('www/build', 'dev');
588622
config.setPublicPath('/build');

test/loaders/typescript.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* This file is part of the Symfony package.
3+
*
4+
* (c) Fabien Potencier <[email protected]>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
'use strict';
11+
12+
const expect = require('chai').expect;
13+
const WebpackConfig = require('../../lib/WebpackConfig');
14+
const RuntimeConfig = require('../../lib/config/RuntimeConfig');
15+
const tsLoader = require('../../lib/loaders/typescript');
16+
17+
function createConfig() {
18+
const runtimeConfig = new RuntimeConfig();
19+
runtimeConfig.context = __dirname;
20+
runtimeConfig.babelRcFileExists = false;
21+
22+
return new WebpackConfig(runtimeConfig);
23+
}
24+
25+
describe('loaders/typescript', () => {
26+
it('getLoaders() basic usage', () => {
27+
const config = createConfig();
28+
config.enableTypeScriptLoader(function(config) {
29+
config.foo = 'bar';
30+
});
31+
32+
const actualLoaders = tsLoader.getLoaders(config);
33+
expect(actualLoaders).to.have.lengthOf(2);
34+
// callback is used
35+
expect(actualLoaders[1].options.foo).to.equal('bar');
36+
});
37+
38+
it('getLoaders() check defaults configuration values', () => {
39+
const config = createConfig();
40+
config.enableTypeScriptLoader(function(config) {
41+
config.foo = 'bar';
42+
});
43+
44+
const actualLoaders = tsLoader.getLoaders(config);
45+
// callback is used
46+
expect(actualLoaders[1].options.foo).to.equal('bar');
47+
// defaults
48+
expect(actualLoaders[1].options.silent).to.be.true;
49+
});
50+
51+
});

yarn.lock

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5281,6 +5281,15 @@ tryit@^1.0.1:
52815281
version "1.0.3"
52825282
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
52835283

5284+
ts-loader@^2.1.0:
5285+
version "2.2.0"
5286+
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-2.2.0.tgz#e5fd8f29b967c4fb42abc76396aa8127a1e49924"
5287+
dependencies:
5288+
colors "^1.0.3"
5289+
enhanced-resolve "^3.0.0"
5290+
loader-utils "^1.0.2"
5291+
semver "^5.0.1"
5292+
52845293
52855294
version "0.0.0"
52865295
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@@ -5324,6 +5333,10 @@ typedarray@^0.0.6:
53245333
version "0.0.6"
53255334
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
53265335

5336+
typescript@^2.3.4:
5337+
version "2.4.0"
5338+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.0.tgz#aef5a8d404beba36ad339abf079ddddfffba86dd"
5339+
53275340
uglify-js@^2.8.27:
53285341
version "2.8.29"
53295342
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"

0 commit comments

Comments
 (0)