Skip to content

Commit

Permalink
feat(tsConfigFile): add ability to specify tsconfig for type checked …
Browse files Browse the repository at this point in the history
…rules, refactor options generation, refactor tests
  • Loading branch information
sonicoder86 committed Dec 1, 2016
1 parent 932fabc commit 4922095
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 95 deletions.
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

Tslint loader for Webpack

## Installation

``` shell
npm install tslint-loader --save-dev
```

## Usage

Apply the tslint loader as pre/postLoader in your webpack configuration:
Expand All @@ -27,11 +33,7 @@ module.exports = {
quotemark: [true, 'double']
}
},

// enables type checked rules like 'for-in-array'
// uses tsconfig.json from current working directory
typeCheck: false,


// can specify a custom config file relative to current directory
// 'tslint-custom.json'
configFile: false,
Expand All @@ -43,14 +45,22 @@ module.exports = {
// tslint does not interrupt the compilation by default
// if you want any file with tslint errors to fail
// set failOnHint to true
failOnHint: true,
failOnHint: true,

// enables type checked rules like 'for-in-array'
// uses tsconfig.json from current working directory
typeCheck: false,

// can specify a custom tsconfig file relative to current directory
// to be used with type checked rules
tsConfigFile: 'tsconfig.json',

// name of your formatter (optional)
formatter: 'yourformatter',

// path to directory containing formatter (optional)
formattersDirectory: 'node_modules/tslint-loader/formatters/',

// These options are useful if you want to save output to files
// for your continuous integration server
fileOutput: {
Expand All @@ -74,14 +84,9 @@ module.exports = {
}
}
```
## Installation

``` shell
npm install tslint-loader --save-dev
```

## License

MIT (http://www.opensource.org/licenses/mit-license.php)
[MIT](http://www.opensource.org/licenses/mit-license.php)


32 changes: 17 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,38 @@ var objectAssign = require('object-assign');

function resolveOptions(webpackInstance) {
var tslintOptions = webpackInstance.options.tslint ? webpackInstance.options.tslint : {};
var configFile = tslintOptions.configFile
? path.resolve(process.cwd(), tslintOptions.configFile)
: null;
var query = loaderUtils.parseQuery(webpackInstance.query);

var options = {
formatter: 'custom',
formattersDirectory: __dirname + '/formatters/',
configuration: Lint.Linter.findConfiguration(configFile, webpackInstance.resourcePath).results
};
var options = objectAssign({}, tslintOptions, query);

objectAssign(options, tslintOptions);
var configFile = options.configFile
? path.resolve(process.cwd(), options.configFile)
: null;

// Override options in tslint.json by those passed to the loader as a query string
var query = loaderUtils.parseQuery(webpackInstance.query);
objectAssign(options, query);
options.formatter = options.formatter || 'custom';
options.formattersDirectory = options.formattersDirectory || __dirname + '/formatters/';
options.configuration = options.configuration || Lint.Linter.findConfiguration(configFile, webpackInstance.resourcePath).results;
options.tsConfigFile = options.tsConfigFile || 'tsconfig.json';

return options;
}

function lint(webpackInstance, input, options) {
var newLintOptions = { fix: false, formatter: options.formatter || 'custom', formattersDirectory: options.formattersDirectory || __dirname + '/formatters/', rulesDirectory: '' };
var lintOptions = {
fix: false,
formatter: options.formatter,
formattersDirectory: options.formattersDirectory,
rulesDirectory: ''
};
var bailEnabled = (webpackInstance.options.bail === true);

var program;
if (options.typeCheck) {
var tsconfigPath = path.resolve(process.cwd(), 'tsconfig.json');
var tsconfigPath = path.resolve(process.cwd(), options.tsConfigFile);
program = Lint.Linter.createProgram(tsconfigPath);
}

var linter = new Lint.Linter(newLintOptions, program);
var linter = new Lint.Linter(lintOptions, program);
linter.lint(webpackInstance.resourcePath, input, options.configuration);
var result = linter.getResult();
var emitter = options.emitErrors ? webpackInstance.emitError : webpackInstance.emitWarning;
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@
"rimraf": "^2.4.4"
},
"devDependencies": {
"awesome-typescript-loader": "^3.0.0-beta.3",
"awesome-typescript-loader": "^3.0.0-beta.9",
"chai": "^3.5.0",
"enhanced-resolve": "^2.3.0",
"es6-promisify": "^5.0.0",
"eslint": "3.11.1",
"mocha": "^3.1.2",
"mocha": "^3.2.0",
"np": "^2.10.1",
"tslint": "^4.0.0",
"typescript": "^2.0.10",
"typescript": "^2.1.1",
"webpack": "^1.13.3"
}
}
113 changes: 50 additions & 63 deletions test/loader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

var path = require('path');
var expect = require('chai').expect;
var webpack = require('webpack');
var assign = require('object-assign');
var webpackConfig = require('./webpack.config');
var webpackRunner = require('./webpack-runner');

describe('TslintLoader', function() {
it('should lint typescript files and output warning', function(done) {
webpack(webpackConfig, function(err, stats) {
if (err) return done(err);

it('should lint typescript files and output warning', function() {
return webpackRunner().then(function(stats) {
expect(stats.hasErrors()).to.be.false;
expect(stats.hasWarnings()).to.be.true;

Expand All @@ -20,69 +16,53 @@ describe('TslintLoader', function() {
expect(result.warnings).to.eql([
'./test/app/engine.ts\n[8, 1]: Calls to \'console.log\' are not allowed.\n'
]);
done();
});
});

it('should overwrite configuration in tslint json', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should overwrite configuration in tslint json', function() {
return webpackRunner({
tslint: {
configuration: {
rules: {
'no-console': [false]
}
}
}
});

webpack(localConfig, function(err, stats) {
if (err) return done(err);

}).then(function(stats) {
expect(stats.hasErrors()).to.be.false;
expect(stats.hasWarnings()).to.be.false;
done();
});
});

it('should use custom tslint file when option given', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should use custom tslint file when option given', function() {
return webpackRunner({
tslint: {
configFile: 'tslint-custom.json'
}
});

webpack(localConfig, function(err, stats) {
if (err) return done(err);

}).then(function(stats) {
expect(stats.hasErrors()).to.be.false;
expect(stats.hasWarnings()).to.be.false;
done();
});
});

it('should emit linting failure as error when forced to', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should emit linting failure as error when forced to', function() {
return webpackRunner({
tslint: {
emitErrors: true
}
});

webpack(localConfig, function(err, stats) {
if (err) return done(err);

}).then(function(stats) {
expect(stats.hasErrors()).to.be.true;
expect(stats.hasWarnings()).to.be.false;

var result = stats.toJson();
expect(result.errors).to.eql([
'./test/app/engine.ts\n[8, 1]: Calls to \'console.log\' are not allowed.\n'
]);
done();
});
});

it('should accept options from query string also', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should accept options from query string also', function() {
return webpackRunner({
module: {
preLoaders: [
{
Expand All @@ -93,49 +73,40 @@ describe('TslintLoader', function() {
loaders: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
loader: 'awesome-typescript-loader',
query: { forkCheckerSilent: true }
}
]
}
});

webpack(localConfig, function(err, stats) {
if (err) return done(err);

}).then(function(stats) {
expect(stats.hasErrors()).to.be.true;
expect(stats.hasWarnings()).to.be.false;

var result = stats.toJson();
expect(result.errors).to.eql([
'./test/app/engine.ts\n[8, 1]: Calls to \'console.log\' are not allowed.\n'
]);
done();
});
});

it('should fail on linting failure when forced to', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should fail on linting failure when forced to', function() {
return webpackRunner({
tslint: {
failOnHint: true
}
});

webpack(localConfig, function(err, stats) {
if (err) return done(err);

}).then(function(stats) {
expect(stats.hasErrors()).to.be.true;
expect(stats.hasWarnings()).to.be.true;

var result = stats.toJson();
expect(result.assets.length).to.eql(0);
expect(result.chunks.length).to.eql(0);
expect(result.errors[0]).to.contain('Module build failed: Error: Compilation failed due to tslint errors.');
done();
});
});

it('should use type checked rules when forced to', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should use type checked rules when forced to', function() {
return webpackRunner({
entry: {
engine: path.resolve(__dirname, 'app', 'for-in-array.ts')
},
Expand All @@ -147,11 +118,33 @@ describe('TslintLoader', function() {
}
}
}
});
}).then(function(stats) {
expect(stats.hasErrors()).to.be.false;
expect(stats.hasWarnings()).to.be.true;

var result = stats.toJson();

webpack(localConfig, function(err, stats) {
if (err) return done(err);
expect(result.warnings).to.eql([
'./test/app/for-in-array.ts\n[4, 1]: for-in loops over arrays are forbidden. Use for-of or array.forEach instead.\n'
]);
});
});

it('should use type checked rules also with custom tsconfig file', function() {
return webpackRunner({
entry: {
engine: path.resolve(__dirname, 'app', 'for-in-array.ts')
},
tslint: {
typeCheck: true,
tsConfigFile: 'test/tsconfig.json',
configuration: {
rules: {
'no-for-in-array': true
}
}
}
}).then(function(stats) {
expect(stats.hasErrors()).to.be.false;
expect(stats.hasWarnings()).to.be.true;

Expand All @@ -160,26 +153,20 @@ describe('TslintLoader', function() {
expect(result.warnings).to.eql([
'./test/app/for-in-array.ts\n[4, 1]: for-in loops over arrays are forbidden. Use for-of or array.forEach instead.\n'
]);
done();
});
});

it('should use custom formatter with custom directory', function(done) {
var localConfig = assign({}, webpackConfig, {
it('should use custom formatter with custom directory', function() {
return webpackRunner({
tslint: {
formattersDirectory: 'test/formatters/',
formatter: 'simple',
}
});

webpack(localConfig, function(err, stats) {
if (err) return done(err);

}).then(function(stats) {
var result = stats.toJson();
expect(result.warnings).to.eql([
'./test/app/engine.ts\nCalls to \'console.log\' are not allowed.\n'
]);
done();
});
});
});
15 changes: 15 additions & 0 deletions test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compileOnSave": false,
"buildOnSave": false,
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true
},
"exclude": [
"node_modules",
"dist"
]
}
14 changes: 14 additions & 0 deletions test/webpack-runner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

var webpack = require('webpack');
var assign = require('object-assign');
var promisify = require('es6-promisify');
var webpackConfig = require('./webpack.config');

module.exports = function(additionalConfig) {
additionalConfig = additionalConfig || {};

return promisify(webpack)(
assign({}, webpackConfig, additionalConfig)
);
};
Loading

0 comments on commit 4922095

Please sign in to comment.