Skip to content

Support linting + formatting with pre-commit hook #169

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 5 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 12 additions & 18 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
{
"env": {
"node": true
},
"extends": "eslint:recommended",
"rules": {
"indent": [
"error",
2
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
"parserOptions": {
"ecmaVersion": 6,
"ecmaFeatures": {
"experimentalObjectRestSpread": true
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should be removed once #166 is merged, or the object spread is migrated to Object.assign in index.spec.js#L26 and vue.spec.js#27 due to node6 compat

}
}
},
"env": {
"node": true,
"mocha": true,
"es6": true
},
"extends": "eslint:recommended"
}
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"singleQuote": true,
"semi": true,
"tabWidth": 2,
"useTabs": false,
"printWidth": 80
}
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ install:
- yarn install
- yarn build
- yarn add $WEBPACK $TSLOADER $VUELOADER -D
- yarn lint
env:
- WEBPACK=webpack@^4.0.0 TSLOADER=ts-loader@^4.3.0 VUELOADER=vue-loader@^15.2.4
- WEBPACK=webpack@^3.10.0 TSLOADER=ts-loader@^3.4.0 VUELOADER=vue-loader@^13.5.0
Expand Down
32 changes: 28 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"test": "npm run build && npm run test:unit && npm run test:integration",
"test:watch": "mocha -R spec --watch ./test/unit",
"test:coverage": "rimraf coverage && istanbul cover -root lib --include-all-sources mocha -- -R spec ./test/unit ./test/integration",
"lint": "eslint ./lib ./test",
"lint:fix": "eslint ./lib ./test --fix"
"lint": "tslint --project src/tsconfig.json && eslint ./test",
"lint:fix": "tslint --project src/tsconfig.json --fix && eslint ./test --fix"
},
"repository": {
"url": "https://github.com/Realytics/fork-ts-checker-webpack-plugin.git",
Expand Down Expand Up @@ -61,15 +61,19 @@
"@types/webpack": "^4.4.9",
"chai": "^3.5.0",
"css-loader": "^0.28.7",
"eslint": "^3.19.0",
"eslint": "^5.7.0",
"husky": "^1.1.2",
"istanbul": "^0.4.5",
"lint-staged": "^7.3.0",
"mocha": "^3.4.1",
"mock-fs": "^4.3.0",
"mock-require": "^2.0.2",
"prettier": "^1.14.3",
"rimraf": "^2.5.4",
"sinon": "^2.3.1",
"ts-loader": "4.3.0",
"tslint": "^5.0.0",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"typescript": "^3.0.1",
"vue": "^2.5.16",
"vue-class-component": "^6.1.1",
Expand All @@ -93,5 +97,25 @@
"minimatch": "^3.0.4",
"resolve": "^1.5.0",
"tapable": "^1.0.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"concurrent": false,
"linters": {
"*.js": [
"eslint --fix"
],
"*.ts": [
"tslint --fix"
],
"*.{js,ts}": [
"prettier --write",
"git add"
]
}
}
}
8 changes: 3 additions & 5 deletions src/CancellationToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@ export class CancellationToken {
lastCancellationCheckTime: number;
constructor(cancellationFileName: string, isCancelled: boolean) {
this.isCancelled = !!isCancelled;
this.cancellationFileName = cancellationFileName || crypto.randomBytes(64).toString('hex');
this.cancellationFileName =
cancellationFileName || crypto.randomBytes(64).toString('hex');
this.lastCancellationCheckTime = 0;
}

static createFromJSON(json: CancellationTokenData) {
return new CancellationToken(
json.cancellationFileName,
json.isCancelled
);
return new CancellationToken(json.cancellationFileName, json.isCancelled);
}

toJSON() {
Expand Down
3 changes: 1 addition & 2 deletions src/FilesRegister.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface DataShape {
}

export class FilesRegister {
files: { [filePath: string]: { mtime: number; data: DataShape; }};
files: { [filePath: string]: { mtime: number; data: DataShape } };
dataFactory: (_data?: any) => DataShape; // It doesn't seem that the _data parameter is ever used?

constructor(dataFactory: (_data?: any) => DataShape) {
Expand Down Expand Up @@ -74,4 +74,3 @@ export class FilesRegister {
}
}
}

10 changes: 5 additions & 5 deletions src/FilesWatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ export class FilesWatcher {
}

this.watchers = this.watchPaths.map((watchPath: string) => {
return chokidar.watch(
watchPath,
{ persistent: true, alwaysStat: true }
)
return chokidar
.watch(watchPath, { persistent: true, alwaysStat: true })
.on('change', (filePath: string, stats: any) => {
if (this.isFileSupported(filePath)) {
(this.listeners['change'] || []).forEach(changeListener => {
Expand Down Expand Up @@ -67,7 +65,9 @@ export class FilesWatcher {

off(event: string, listener: Function) {
if (this.listeners[event]) {
this.listeners[event] = this.listeners[event].filter(oldListener => oldListener !== listener);
this.listeners[event] = this.listeners[event].filter(
oldListener => oldListener !== listener
);
}
}
}
118 changes: 85 additions & 33 deletions src/IncrementalChecker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,19 @@ export class IncrementalChecker {

// it's shared between compilations
this.files = new FilesRegister(() => ({
// data shape
source: undefined,
linted: false,
lints: []
// data shape
source: undefined,
linted: false,
lints: []
}));
}

static loadProgramConfig(configFile: string) {
return ts.parseJsonConfigFileContent(
// Regardless of the setting in the tsconfig.json we want isolatedModules to be false
Object.assign(ts.readConfigFile(configFile, ts.sys.readFile).config, { isolatedModules: false }),
Object.assign(ts.readConfigFile(configFile, ts.sys.readFile).config, {
isolatedModules: false
}),
ts.sys,
path.dirname(configFile)
);
Expand All @@ -80,7 +82,9 @@ export class IncrementalChecker {
static loadLinterConfig(configFile: string): ConfigurationFile {
const tslint = require('tslint');

return tslint.Configuration.loadConfigurationFromPath(configFile) as ConfigurationFile;
return tslint.Configuration.loadConfigurationFromPath(
configFile
) as ConfigurationFile;
}

static createProgram(
Expand All @@ -107,7 +111,7 @@ export class IncrementalChecker {

// get source file only if there is no source in files register
if (!files.has(filePath) || !files.getData(filePath).source) {
files.mutateData(filePath, (data) => {
files.mutateData(filePath, data => {
data.source = realGetSourceFile(filePath, languageVersion, onError);
});
}
Expand All @@ -129,13 +133,21 @@ export class IncrementalChecker {
return new tslint.Linter({ fix: false }, program);
}

static isFileExcluded(filePath: string, linterExclusions: minimatch.IMinimatch[]): boolean {
return endsWith(filePath, '.d.ts') || linterExclusions.some(matcher => matcher.match(filePath));
static isFileExcluded(
filePath: string,
linterExclusions: minimatch.IMinimatch[]
): boolean {
return (
endsWith(filePath, '.d.ts') ||
linterExclusions.some(matcher => matcher.match(filePath))
);
}

nextIteration() {
if (!this.watcher) {
const watchExtensions = this.vue ? ['.ts', '.tsx', '.vue'] : ['.ts', '.tsx'];
const watchExtensions = this.vue
? ['.ts', '.tsx', '.vue']
: ['.ts', '.tsx'];
this.watcher = new FilesWatcher(this.watchPaths, watchExtensions);

// connect watcher with register
Expand All @@ -150,13 +162,20 @@ export class IncrementalChecker {
}

if (!this.linterConfig && this.linterConfigFile) {
this.linterConfig = IncrementalChecker.loadLinterConfig(this.linterConfigFile);

if (this.linterConfig.linterOptions && this.linterConfig.linterOptions.exclude) {
this.linterConfig = IncrementalChecker.loadLinterConfig(
this.linterConfigFile
);

if (
this.linterConfig.linterOptions &&
this.linterConfig.linterOptions.exclude
) {
// Pre-build minimatch patterns to avoid additional overhead later on.
// Note: Resolving the path is required to properly match against the full file paths,
// and also deals with potential cross-platform problems regarding path separators.
this.linterExclusions = this.linterConfig.linterOptions.exclude.map(pattern => new minimatch.Minimatch(path.resolve(pattern)));
this.linterExclusions = this.linterConfig.linterOptions.exclude.map(
pattern => new minimatch.Minimatch(path.resolve(pattern))
);
}
}

Expand All @@ -168,7 +187,9 @@ export class IncrementalChecker {
}

loadVueProgram() {
this.programConfig = this.programConfig || VueProgram.loadProgramConfig(this.programConfigFile);
this.programConfig =
this.programConfig ||
VueProgram.loadProgramConfig(this.programConfigFile);

return VueProgram.createProgram(
this.programConfig,
Expand All @@ -180,9 +201,16 @@ export class IncrementalChecker {
}

loadDefaultProgram() {
this.programConfig = this.programConfig || IncrementalChecker.loadProgramConfig(this.programConfigFile);
this.programConfig =
this.programConfig ||
IncrementalChecker.loadProgramConfig(this.programConfigFile);

return IncrementalChecker.createProgram(this.programConfig, this.files, this.watcher, this.program);
return IncrementalChecker.createProgram(
this.programConfig,
this.files,
this.watcher,
this.program
);
}

hasLinter() {
Expand All @@ -195,18 +223,30 @@ export class IncrementalChecker {
const filesToCheck = this.program.getSourceFiles();

// calculate subset of work to do
const workSet = new WorkSet(filesToCheck, this.workNumber, this.workDivision);
const workSet = new WorkSet(
filesToCheck,
this.workNumber,
this.workDivision
);

// check given work set
workSet.forEach(sourceFile => {
if (cancellationToken) {
cancellationToken.throwIfCancellationRequested();
}

const diagnosticsToRegister: ReadonlyArray<ts.Diagnostic> = this.checkSyntacticErrors
const diagnosticsToRegister: ReadonlyArray<ts.Diagnostic> = this
.checkSyntacticErrors
? []
.concat(this.program.getSemanticDiagnostics(sourceFile, cancellationToken))
.concat(this.program.getSyntacticDiagnostics(sourceFile, cancellationToken))
.concat(
this.program.getSemanticDiagnostics(sourceFile, cancellationToken)
)
.concat(
this.program.getSyntacticDiagnostics(
sourceFile,
cancellationToken
)
)
: this.program.getSemanticDiagnostics(sourceFile, cancellationToken);

diagnostics.push.apply(diagnostics, diagnosticsToRegister);
Expand All @@ -224,12 +264,20 @@ export class IncrementalChecker {
}

// select files to lint
const filesToLint = this.files.keys().filter(filePath =>
!this.files.getData(filePath).linted && !IncrementalChecker.isFileExcluded(filePath, this.linterExclusions)
);
const filesToLint = this.files
.keys()
.filter(
filePath =>
!this.files.getData(filePath).linted &&
!IncrementalChecker.isFileExcluded(filePath, this.linterExclusions)
);

// calculate subset of work to do
const workSet = new WorkSet(filesToLint, this.workNumber, this.workDivision);
const workSet = new WorkSet(
filesToLint,
this.workNumber,
this.workDivision
);

// lint given work set
workSet.forEach(fileName => {
Expand All @@ -239,11 +287,11 @@ export class IncrementalChecker {
this.linter.lint(fileName, undefined, this.linterConfig);
} catch (e) {
if (
fs.existsSync(fileName) &&
// check the error type due to file system lag
!(e instanceof Error) &&
!(e.constructor.name === 'FatalError') &&
!(e.message && e.message.trim().startsWith("Invalid source file"))
fs.existsSync(fileName) &&
// check the error type due to file system lag
!(e instanceof Error) &&
!(e.constructor.name === 'FatalError') &&
!(e.message && e.message.trim().startsWith('Invalid source file'))
) {
// it's not because file doesn't exist - throw error
throw e;
Expand All @@ -269,9 +317,13 @@ export class IncrementalChecker {
});

// get all lints
const lints = this.files.keys().reduce((innerLints, filePath) =>
innerLints.concat(this.files.getData(filePath).lints),
[]);
const lints = this.files
.keys()
.reduce(
(innerLints, filePath) =>
innerLints.concat(this.files.getData(filePath).lints),
[]
);

// normalize and deduplicate lints
return NormalizedMessage.deduplicate(
Expand Down
4 changes: 2 additions & 2 deletions src/Message.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NormalizedMessage } from './NormalizedMessage';

export interface Message {
diagnostics: NormalizedMessage[];
lints: NormalizedMessage[];
diagnostics: NormalizedMessage[];
lints: NormalizedMessage[];
}
Loading