Skip to content
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

fix: use correct lint config for individual files #167

Merged
merged 4 commits into from Jun 7, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 46 additions & 20 deletions src/lint.ts
Expand Up @@ -28,28 +28,54 @@ import {Options} from './cli';
*/
export function lint(
options: Options, files: string[] = [], fix = false): boolean {
const configPath =
fs.existsSync(path.join(options.targetRootDir, 'tslint.json')) ?
path.join(options.targetRootDir, 'tslint.json') :
path.join(options.gtsRootDir, 'tslint.json');

const program = createProgram(options);
const configuration = Configuration.findConfiguration(configPath, '').results;
const linter = new Linter({fix, formatter: 'codeFrame'}, program);
const srcFiles = files.length > 0 ? files : Linter.getFileNames(program);
srcFiles.forEach(file => {
const sourceFile = program.getSourceFile(file);
if (sourceFile) {
const fileContents = sourceFile.getFullText();
linter.lint(file, fileContents, configuration);
if (files.length > 0) { // manually provided filenames.
const rcs = files.map(file => {
// Different config files may apply to each file.
const configPath = Configuration.findConfigurationPath(null, file) ||
path.join(options.gtsRootDir, 'tslint.json');

const configuration =
Configuration.loadConfigurationFromPath(configPath, '');
const source = fs.readFileSync(file, 'utf8');

const linter = new Linter({fix, formatter: 'codeFrame'});
linter.lint(file, source, configuration);
const result = linter.getResult();
if (result.errorCount || result.warningCount) {
options.logger.log(result.output);
return false;
}
return true;
});

return rcs.every(rc => rc); // if all files succeeded.
} else {
// Lint the set of files specified by the typescript program config.
const program = createProgram(options);
files = Linter.getFileNames(program);

const configPath =
fs.existsSync(path.join(options.targetRootDir, 'tslint.json')) ?
path.resolve(options.targetRootDir, 'tslint.json') :
path.resolve(options.gtsRootDir, 'tslint.json');

const configuration = Configuration.loadConfigurationFromPath(configPath);
const linter = new Linter({fix, formatter: 'codeFrame'}, program);

files.forEach(file => {
const sourceFile = program.getSourceFile(file);
if (sourceFile) {
const fileContents = sourceFile.getFullText();
linter.lint(file, fileContents, configuration);
}
});
const result = linter.getResult();
if (result.errorCount || result.warningCount) {
options.logger.log(result.output);
return false;
}
});
const result = linter.getResult();
if (result.errorCount || result.warningCount) {
options.logger.log(result.output);
return false;
return true;
}
return true;
}

export function createProgram(options: Options): ts.Program {
Expand Down
45 changes: 39 additions & 6 deletions test/test-lint.ts
Expand Up @@ -135,20 +135,21 @@ test.serial('lint should lint only specified files', async t => {
});
});

test.serial('lint should not throw for unrecognized files', async t => {
test.serial('lint should throw for unrecognized files', async t => {
await withFixtures(
{
'tsconfig.json': JSON.stringify({}),
'a.ts': GOOD_CODE,
},
async () => {
lint.lint(OPTIONS, ['z.ts']);
t.pass();
t.throws(() => {
lint.lint(OPTIONS, ['z.ts']);
});
});
});

test.serial('lint should prefer user config file over default', async t => {
const CUSTOM_LINT_CODE = 'const t: Object;';
const CUSTOM_LINT_CODE = 'debugger;';

// By defualt the above should fail lint.
await withFixtures(
Expand All @@ -158,7 +159,7 @@ test.serial('lint should prefer user config file over default', async t => {
},
async () => {
const okay = lint.lint(OPTIONS);
t.is(okay, false);
t.false(okay);
});

// User should be able to override the default config.
Expand All @@ -170,8 +171,40 @@ test.serial('lint should prefer user config file over default', async t => {
},
async () => {
const okay = lint.lint(OPTIONS);
t.is(okay, true);
t.true(okay);
});
});

test.serial(
'lint for specific files should use file-specific config', async t => {
const CODE_WITH_PARSEINT = 'parseInt(42);';
let logBuffer = '';
const optionsWithLog = Object.assign({}, OPTIONS, {
logger: {
log: (...args: string[]) => {
logBuffer += (args.join(' '));
},
error: nop,
dir: nop
}
});
await withFixtures(
{
dira: {
'a.ts': CODE_WITH_PARSEINT,
// no tslint, so default should apply.
},
dirb: {
'b.ts': CODE_WITH_PARSEINT,
'tslint.json': JSON.stringify({})
}
},
async () => {
const okay = lint.lint(optionsWithLog, ['dira/a.ts', 'dirb/b.ts']);
t.false(okay);
t.regex(logBuffer, /dira\/a\.ts/);
t.notRegex(logBuffer, /dirb\/b\.ts/);
});
});

// TODO: test for when tsconfig.json is missing.