Skip to content

Add support to enableTypescriptLoader #50

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Jun 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions fixtures/js/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import render = require('./render');

render();
5 changes: 5 additions & 0 deletions fixtures/js/render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function render() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about renaming this to render.tsx - it would slightly enhance the test, since it would take advantage of the resolve.extensions change and make sure the loader applies to both extensions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good idea! 👍

document.getElementById('app').innerHTML = "<h1>Welcome to Your TypeScript App</h1>";
}

export = render;
5 changes: 5 additions & 0 deletions fixtures/js/render2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function render() {
document.getElementById('app').innerHTML = "<h1>Welcome to Your TypeScript App</h1>";
}

export = render;
3 changes: 3 additions & 0 deletions fixtures/js/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"compilerOptions": {}
}
17 changes: 17 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,23 @@ module.exports = {
return this;
},

/**
* Call this if you plan on loading TypeScript files.
*
* Encore.enableTypeScriptLoader(function(tsConfig) {
* // change the tsConfig
* });
*
* Supported configuration options:
* @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#available-options
*
* @param {function} callback
* @return {exports}
*/
enableTypeScriptLoader(callback) {
webpackConfig.enableTypeScriptLoader(callback);
}

/**
* If enabled, the Vue.js loader is enabled.
*
Expand Down
12 changes: 12 additions & 0 deletions lib/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ class WebpackConfig {
this.useVueLoader = false;
this.vueLoaderOptionsCallback = () => {};
this.loaders = [];
this.useTypeScriptLoader = false;
this.tsConfigurationCallback = function() {};
}

getContext() {
Expand Down Expand Up @@ -224,6 +226,16 @@ class WebpackConfig {
this.useReact = true;
}

enableTypeScriptLoader(callback) {
this.useTypeScriptLoader = true;

if (typeof callback !== 'function') {
throw new Error('Argument 1 to enableTypeScriptLoader() must be a callback function.');
}

this.tsConfigurationCallback = callback;
}

enableVueLoader(vueLoaderOptionsCallback = () => {}) {
this.useVueLoader = true;

Expand Down
11 changes: 10 additions & 1 deletion lib/config-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const cssLoaderUtil = require('./loaders/css');
const sassLoaderUtil = require('./loaders/sass');
const lessLoaderUtil = require('./loaders/less');
const babelLoaderUtil = require('./loaders/babel');
const tsLoaderUtil = require('./loaders/typescript');
const vueLoaderUtil = require('./loaders/vue');

class ConfigGenerator {
Expand Down Expand Up @@ -72,7 +73,7 @@ class ConfigGenerator {
config.stats = this.buildStatsConfig();

config.resolve = {
extensions: ['.js', '.jsx', '.vue'],
extensions: ['.js', '.jsx', '.vue', '.ts', '.tsx'],
alias: {}
};

Expand Down Expand Up @@ -161,6 +162,14 @@ class ConfigGenerator {
});
}

if (this.webpackConfig.useTypeScriptLoader) {
this.webpackConfig.addLoader({
test: /\.tsx?$/,
exclude: /node_modules/,
use: tsLoaderUtil.getLoaders(this.webpackConfig)
});
}

this.webpackConfig.loaders.forEach((loader) => {
rules.push(loader);
});
Expand Down
4 changes: 4 additions & 0 deletions lib/friendly-errors/transformers/missing-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ function transform(error) {
case 'jsx':
error.loaderName = 'react';
break;
case 'tsx':
case 'ts':
error.loaderName = 'typescript';
break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch on this! As it looks like you discovered, this is what gives us the good error when you try to load a .tsx file and you haven't enabled its loader :)

// add more as needed
default:
return error;
Expand Down
5 changes: 5 additions & 0 deletions lib/loader-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ const loaderFeatures = {
packages: ['babel-preset-react'],
description: 'process React JS files'
},
typescript: {
method: 'enableTypeScriptLoader()',
packages: ['typescript', 'ts-loader'],
description: 'process TypeScript files'
},
vue: {
method: 'enableVueLoader()',
// vue is needed so the end-user can do things
Expand Down
46 changes: 46 additions & 0 deletions lib/loaders/typescript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

'use strict';

const loaderFeatures = require('../loader-features');
const babelLoader = require('./babel');

/**
* @param {WebpackConfig} webpackConfig
* @return {Array} of loaders to use for TypeScript
*/
module.exports = {
getLoaders(webpackConfig) {
loaderFeatures.ensureLoaderPackagesExist('typescript');

// some defaults
let config = {
silent: true,
};

// allow for ts-loader config to be controlled
webpackConfig.tsConfigurationCallback.apply(
// use config as the this variable
config,
[config]
);

// use ts alongside with babel
// @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#babel
let loaders = babelLoader.getLoaders(webpackConfig);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love the note + link 👍

return loaders.concat([
{
loader: 'ts-loader',
// @see https://github.com/TypeStrong/ts-loader/blob/master/README.md#available-options
options: config
}
]);
}
};
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"loader-utils": "^1.1.0",
"lodash": ">=3.5 <5",
"pkg-up": "^1.0.0",
"pretty-error": "^2.1.0",
"pretty-error": "^2.1.1",
"resolve-url-loader": "^2.0.2",
"style-loader": "^0.13.2",
"webpack": "^2.2.0",
Expand All @@ -64,6 +64,8 @@
"postcss-loader": "^1.3.3",
"sass-loader": "^6.0.3",
"sinon": "^2.3.4",
"ts-loader": "^2.1.0",
"typescript": "^2.3.4",
"vue": "^2.3.4",
"vue-loader": "^12.2.1",
"vue-template-compiler": "^2.3.4",
Expand Down
21 changes: 21 additions & 0 deletions test/WebpackConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,27 @@ describe('WebpackConfig object', () => {
});
});

describe('enableTypeScriptLoader', () => {
it('Calling method sets it', () => {
const config = createConfig();
const testCallback = () => {};
config.enableTypeScriptLoader(testCallback);
expect(config.tsConfigurationCallback).to.equal(testCallback);
});

it('Calling with non-callback throws an error', () => {
const config = createConfig();

expect(() => {
config.enableTypeScriptLoader('FOO');
}).to.throw('must be a callback function');

expect(() => {
config.enableTypeScriptLoader();
}).to.throw('must be a callback function');
});
});

describe('addPlugin', () => {
it('extends the current registered plugins', () => {
const config = createConfig();
Expand Down
13 changes: 13 additions & 0 deletions test/friendly-errors/transformers/missing-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,18 @@ describe('transform/missing-loader', () => {
expect(actualError.type).to.deep.equal('loader-not-enabled');
expect(actualError.loaderName).to.deep.equal('sass');
});

it('Typescript error is properly transformed', () => {
const startError = {
name: 'ModuleParseError',
message: 'You may need an appropriate loader',
file: '/path/to/file.ts'
};
const actualError = transform(Object.assign({}, startError));

expect(actualError.name).to.deep.equal('Loader not enabled');
expect(actualError.type).to.deep.equal('loader-not-enabled');
expect(actualError.loaderName).to.deep.equal('typescript');
});
});
});
36 changes: 35 additions & 1 deletion test/functional.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('Functional tests using webpack', function() {
testSetup.emptyTmpDir();
});

describe('Basic scenarios', () => {
describe('Basic scenarios.', () => {

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

it('When configured, TypeScript is compiled!', (done) => {
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', ['./js/render.ts', './js/index.ts']);
const testCallback = () => {};
config.enableTypeScriptLoader(testCallback);

testSetup.runWebpack(config, (webpackAssert) => {
// check that ts-loader transformed the ts file
webpackAssert.assertOutputFileContains(
'main.js',
'document.getElementById(\'app\').innerHTML = "<h1>Welcome to Your TypeScript App</h1>";'
);

expect(config.outputPath).to.be.a.directory().with.deep.files([
'main.js',
'manifest.json'
]);

testSetup.requestTestPage(
path.join(config.getContext(), 'www'),
[
'build/main.js'
],
(browser) => {

// assert that the ts module rendered
browser.assert.text('#app h1', 'Welcome to Your TypeScript App');
done();
}
);
});
});

it('The output directory is cleaned between builds', (done) => {
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
Expand Down
51 changes: 51 additions & 0 deletions test/loaders/typescript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

'use strict';

const expect = require('chai').expect;
const WebpackConfig = require('../../lib/WebpackConfig');
const RuntimeConfig = require('../../lib/config/RuntimeConfig');
const tsLoader = require('../../lib/loaders/typescript');

function createConfig() {
const runtimeConfig = new RuntimeConfig();
runtimeConfig.context = __dirname;
runtimeConfig.babelRcFileExists = false;

return new WebpackConfig(runtimeConfig);
}

describe('loaders/typescript', () => {
it('getLoaders() basic usage', () => {
const config = createConfig();
config.enableTypeScriptLoader(function(config) {
config.foo = 'bar';
});

const actualLoaders = tsLoader.getLoaders(config);
expect(actualLoaders).to.have.lengthOf(2);
// callback is used
expect(actualLoaders[1].options.foo).to.equal('bar');
});

it('getLoaders() check defaults configuration values', () => {
const config = createConfig();
config.enableTypeScriptLoader(function(config) {
config.foo = 'bar';
});

const actualLoaders = tsLoader.getLoaders(config);
// callback is used
expect(actualLoaders[1].options.foo).to.equal('bar');
// defaults
expect(actualLoaders[1].options.silent).to.be.true;
});

});
13 changes: 13 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5281,6 +5281,15 @@ tryit@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"

ts-loader@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-2.2.0.tgz#e5fd8f29b967c4fb42abc76396aa8127a1e49924"
dependencies:
colors "^1.0.3"
enhanced-resolve "^3.0.0"
loader-utils "^1.0.2"
semver "^5.0.1"

[email protected]:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
Expand Down Expand Up @@ -5324,6 +5333,10 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"

typescript@^2.3.4:
version "2.4.0"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.0.tgz#aef5a8d404beba36ad339abf079ddddfffba86dd"

uglify-js@^2.8.27:
version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
Expand Down