diff --git a/config/tslint.config.js b/config/tslint.config.js new file mode 100644 index 00000000..7fd6eea3 --- /dev/null +++ b/config/tslint.config.js @@ -0,0 +1,9 @@ +module.exports = { + + tsLintConfig: './tslint.json', + + exclude: [ + '**/node_modules/**/*' + ] + +} diff --git a/src/lint.ts b/src/lint.ts index a55b15d7..9a717352 100644 --- a/src/lint.ts +++ b/src/lint.ts @@ -4,7 +4,7 @@ import { join } from 'path'; import { lintFiles } from './lint/lint-utils'; import { createProgram, getFileNames } from './lint/lint-factory'; import { Logger } from './logger/logger'; -import { getUserConfigFile } from './util/config'; +import { getUserConfigFile, fillConfigDefaults } from './util/config'; import { ENV_BAIL_ON_LINT_ERROR, ENV_TYPE_CHECK_ON_LINT } from './util/constants'; import { getBooleanPropertyValue } from './util/helpers'; import { getTsConfigPath } from './transpile'; @@ -15,17 +15,19 @@ import { runWorker } from './worker-client'; export interface LintWorkerConfig { tsConfig: string; tsLintConfig: string | null; + configFile?: string | null; filePaths?: string[]; typeCheck?: boolean; + exclude?: string[]; } const taskInfo: TaskInfo = { fullArg: '--tslint', shortArg: '-i', - envVar: 'ionic_tslint', - packageConfig: 'IONIC_TSLINT', - defaultConfigFile: '../tslint' + envVar: 'IONIC_TSLINT', + packageConfig: 'ionic_tslint', + defaultConfigFile: 'tslint.config' }; @@ -44,11 +46,15 @@ export function lint(context: BuildContext, tsLintConfig?: string | null, typeCh } export function lintWorker(context: BuildContext, {tsConfig, tsLintConfig, typeCheck}: LintWorkerConfig) { + const configFile: string = getUserConfigFile(context, taskInfo, null); + const { tsLintCofig: tsLintDefaultConfig, exclude } = fillConfigDefaults(configFile, taskInfo.defaultConfigFile); + tsLintConfig = tsLintConfig || tsLintDefaultConfig; return getLintConfig(context, tsLintConfig) .then(tsLintConfig => lintApp(context, { tsConfig, tsLintConfig, - typeCheck + typeCheck, + exclude })); } @@ -58,7 +64,7 @@ export function lintUpdate(changedFiles: ChangedFile[], context: BuildContext, t return runWorker('lint', 'lintUpdateWorker', context, { typeCheck, tsConfig: getTsConfigPath(context), - tsLintConfig: getUserConfigFile(context, taskInfo, null), + tsLintConfig: getTsLintConfig(context, null), filePaths: changedTypescriptFiles.map(changedTypescriptFile => changedTypescriptFile.filePath) }); } @@ -72,16 +78,16 @@ export function lintUpdateWorker(context: BuildContext, {tsConfig, tsLintConfig, } -function lintApp(context: BuildContext, {tsConfig, tsLintConfig, typeCheck}: LintWorkerConfig) { +function lintApp(context: BuildContext, {tsConfig, tsLintConfig, typeCheck, exclude}: LintWorkerConfig) { const program = createProgram(context, tsConfig); - const files = getFileNames(context, program); + const files = getFileNames(context, program, exclude); return lintFiles(context, program, tsLintConfig, files, {typeCheck}); } function getLintConfig(context: BuildContext, tsLintConfig: string | null): Promise { return new Promise((resolve, reject) => { - tsLintConfig = getUserConfigFile(context, taskInfo, tsLintConfig); + tsLintConfig = getTsLintConfig(context); if (!tsLintConfig) { tsLintConfig = join(context.rootDir, 'tslint.json'); } @@ -100,3 +106,12 @@ function getLintConfig(context: BuildContext, tsLintConfig: string | null): Prom }); }); } + +function getTsLintConfig(context: BuildContext, configFile?: string): string { + configFile = getUserConfigFile(context, taskInfo, configFile); + let { tsLintConfig } = fillConfigDefaults(configFile, taskInfo.defaultConfigFile); + if (tsLintConfig) { + tsLintConfig = join(context.rootDir, tsLintConfig); + } + return tsLintConfig; +} diff --git a/src/lint/lint-factory.spec.ts b/src/lint/lint-factory.spec.ts index 72aecf51..524d128c 100644 --- a/src/lint/lint-factory.spec.ts +++ b/src/lint/lint-factory.spec.ts @@ -1,6 +1,7 @@ import { Configuration, Linter } from 'tslint'; import { DiagnosticCategory } from 'typescript'; import * as ts from 'typescript'; +import * as glob from 'glob'; import { isObject } from 'util'; import { createLinter, @@ -78,6 +79,27 @@ describe('lint factory', () => { expect(Array.isArray(files)).toBeTruthy(); expect(files).toEqual(mockFiles); }); + + it('should exclude the files matching the ignore pattern', () => { + const context: any = {rootDir: ''}; + const program = createProgram(context, ''); + const mockFiles = ['test.ts', 'node_modules/folder/file.ts']; + + spyOn(Linter, 'getFileNames').and.returnValue(mockFiles); + const globSyncSpy = spyOn(glob, 'sync').and.callFake((file: string, options: any) => { + return file === mockFiles[0] ? [mockFiles[0]] : []; + }); + + const files = getFileNames(context, program, ['**/node_modules/**/*']); + + expect(Array.isArray(files)).toBeTruthy(); + expect(files).toEqual(mockFiles.slice(0, 1)); + expect(globSyncSpy).toHaveBeenCalledTimes(2); + globSyncSpy.calls.allArgs().map((args, index) => { + expect(args[0]).toBe(mockFiles[index]); + expect(args[1]).toEqual({ignore: ['**/node_modules/**/*'], nodir: true}); + }); + }); }); describe('typeCheck()', () => { diff --git a/src/lint/lint-factory.ts b/src/lint/lint-factory.ts index 38f20052..2fb46594 100644 --- a/src/lint/lint-factory.ts +++ b/src/lint/lint-factory.ts @@ -1,5 +1,6 @@ import { Configuration, Linter, LintResult } from 'tslint'; import { Program, getPreEmitDiagnostics, Diagnostic } from 'typescript'; +import * as glob from 'glob'; import { BuildContext } from '../util/interfaces'; import { isObject } from 'util'; @@ -64,10 +65,18 @@ export function createProgram(context: BuildContext, tsConfig: string): Program * Get all files that are sourced in TS config * @param {BuildContext} context * @param {Program} program + * @param {Array} excludePatterns * @return {Array} */ -export function getFileNames(context: BuildContext, program: Program): string[] { - return Linter.getFileNames(program); +export function getFileNames(context: BuildContext, program: Program, excludePatterns?: string[]): string[] { + let files: string[] = Linter.getFileNames(program); + if (excludePatterns) { + const globOptions = { ignore: excludePatterns, nodir: true }; + files = files + .map((file: string) => glob.sync(file, globOptions)) + .reduce((a: string[], b: string[]) => a.concat(b), []); + } + return files; }