Skip to content

Commit

Permalink
fix(typescript-estree): improve error messaging around project files
Browse files Browse the repository at this point in the history
  • Loading branch information
bradzacher committed Aug 15, 2019
1 parent a8da330 commit ec21ee3
Show file tree
Hide file tree
Showing 18 changed files with 162 additions and 8 deletions.
8 changes: 8 additions & 0 deletions packages/typescript-estree/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@ module.exports = {
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
coverageReporters: ['text-summary', 'lcov'],
globals: {
'ts-jest': {
diagnostics: {
// ignore the diagnostic error for the invalidFileErrors fixtures
ignoreCodes: [5056],
},
},
},
};
2 changes: 1 addition & 1 deletion packages/typescript-estree/src/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class Converter {
*/
constructor(ast: ts.SourceFile, options: ConverterOptions) {
this.ast = ast;
this.options = options;
this.options = { ...options };
}

getASTMaps(): ASTMaps {
Expand Down
39 changes: 36 additions & 3 deletions packages/typescript-estree/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path';
import semver from 'semver';
import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports
import { astConverter } from './ast-converter';
Expand All @@ -9,6 +10,7 @@ import { TSESTree } from './ts-estree';
import {
calculateProjectParserOptions,
createProgram,
defaultCompilerOptions,
} from './tsconfig-parser';

/**
Expand Down Expand Up @@ -87,9 +89,39 @@ function getASTFromProject(
);

if (!astAndProgram && !createDefaultProgram) {
throw new Error(
`If "parserOptions.project" has been set for @typescript-eslint/parser, ${filePath} must be included in at least one of the projects provided.`,
);
// the file was either not matched within the tsconfig, or the extension wasn't expected
const errorLines = [
'"parserOptions.project" has been set for @typescript-eslint/parser.',
`The file does not match your project config: ${filePath}.`,
];
let hasMatchedAnError = false;

const fileExtension = path.extname(filePath);
if (!['.ts', '.tsx', '.js', '.jsx'].includes(fileExtension)) {
const nonStandardExt = `The extension for the file (${fileExtension}) is non-standard`;
if (extra.extraFileExtensions && extra.extraFileExtensions.length > 0) {
if (!extra.extraFileExtensions.includes(fileExtension)) {
errorLines.push(
`${nonStandardExt}. It should be added to your existing "parserOptions.extraFileExtensions".`,
);
hasMatchedAnError = true;
}
} else {
errorLines.push(
`${nonStandardExt}. You should add "parserOptions.extraFileExtensions" to your config.`,
);
hasMatchedAnError = true;
}
}

if (!hasMatchedAnError) {
errorLines.push(
'The file must be included in at least one of the projects provided.',
);
hasMatchedAnError = true;
}

throw new Error(errorLines.join('\n'));
}

return astAndProgram;
Expand Down Expand Up @@ -158,6 +190,7 @@ function createNewProgram(code: string): ASTAndProgram {
noResolve: true,
target: ts.ScriptTarget.Latest,
jsx: extra.jsx ? ts.JsxEmit.Preserve : undefined,
...defaultCompilerOptions,
},
compilerHost,
);
Expand Down
5 changes: 3 additions & 2 deletions packages/typescript-estree/src/tsconfig-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { Extra } from './parser-options';
/**
* Default compiler options for program generation from single root file
*/
const defaultCompilerOptions: ts.CompilerOptions = {
export const defaultCompilerOptions: ts.CompilerOptions = {
allowNonTsExtensions: true,
allowJs: true,
checkJs: true,
};

/**
Expand Down Expand Up @@ -109,7 +110,7 @@ export function calculateProjectParserOptions(
// create compiler host
const watchCompilerHost = ts.createWatchCompilerHost(
tsconfigPath,
/*optionsToExtend*/ { allowNonTsExtensions: true } as ts.CompilerOptions,
defaultCompilerOptions,
ts.sys,
ts.createSemanticDiagnosticsBuilderProgram,
diagnosticReporter,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = true;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"include": [
"ts/included.ts",
"ts/included.tsx",
"js/included.js",
"js/included.jsx",
"other/included.vue"
]
}
36 changes: 36 additions & 0 deletions packages/typescript-estree/tests/lib/__snapshots__/parse.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,42 @@ Object {
}
`;

exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension does not match 1`] = `
"\\"parserOptions.project\\" has been set for @typescript-eslint/parser.
The file does not match your project config: tests/fixtures/invalidFileErrors/other/unknownFileType.unknown.
The extension for the file (.unknown) is non-standard. It should be added to your existing \\"parserOptions.extraFileExtensions\\"."
`;

exports[`parse() invalid file error messages "parserOptions.extraFileExtensions" is non-empty the extension matches the file isn't included 1`] = `
"\\"parserOptions.project\\" has been set for @typescript-eslint/parser.
The file does not match your project config: tests/fixtures/invalidFileErrors/other/notIncluded.vue.
The file must be included in at least one of the projects provided."
`;

exports[`parse() invalid file error messages project includes errors for not included files 1`] = `
"\\"parserOptions.project\\" has been set for @typescript-eslint/parser.
The file does not match your project config: tests/fixtures/invalidFileErrors/ts/notIncluded.ts.
The file must be included in at least one of the projects provided."
`;

exports[`parse() invalid file error messages project includes errors for not included files 2`] = `
"\\"parserOptions.project\\" has been set for @typescript-eslint/parser.
The file does not match your project config: tests/fixtures/invalidFileErrors/ts/notIncluded.tsx.
The file must be included in at least one of the projects provided."
`;

exports[`parse() invalid file error messages project includes errors for not included files 3`] = `
"\\"parserOptions.project\\" has been set for @typescript-eslint/parser.
The file does not match your project config: tests/fixtures/invalidFileErrors/js/notIncluded.js.
The file must be included in at least one of the projects provided."
`;

exports[`parse() invalid file error messages project includes errors for not included files 4`] = `
"\\"parserOptions.project\\" has been set for @typescript-eslint/parser.
The file does not match your project config: tests/fixtures/invalidFileErrors/js/notIncluded.jsx.
The file must be included in at least one of the projects provided."
`;

exports[`parse() non string code should correctly convert code to a string for parse() 1`] = `
Object {
"body": Array [
Expand Down
60 changes: 58 additions & 2 deletions packages/typescript-estree/tests/lib/parse.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { join, resolve, relative } from 'path';
import * as parser from '../../src/parser';
import * as astConverter from '../../src/ast-converter';
import { TSESTreeOptions } from '../../src/parser-options';
import { createSnapshotTestBlock } from '../../tools/test-utils';
import { join } from 'path';

const FIXTURES_DIR = './tests/fixtures/simpleProject';

Expand Down Expand Up @@ -145,7 +145,7 @@ describe('parse()', () => {
};
const projectConfig: TSESTreeOptions = {
...baseConfig,
tsconfigRootDir: join(process.cwd(), FIXTURES_DIR),
tsconfigRootDir: FIXTURES_DIR,
project: './tsconfig.json',
};

Expand Down Expand Up @@ -241,4 +241,60 @@ describe('parse()', () => {
).toBeUndefined();
});
});

describe('invalid file error messages', () => {
const PROJECT_DIR = resolve(FIXTURES_DIR, '../invalidFileErrors');
const code = 'var a = true';
const config: TSESTreeOptions = {
comment: true,
tokens: true,
range: true,
loc: true,
tsconfigRootDir: PROJECT_DIR,
project: './tsconfig.json',
extraFileExtensions: ['.vue'],
};
const testParse = (filePath: string) => (): void => {
parser.parseAndGenerateServices(code, {
...config,
filePath: relative(process.cwd(), join(PROJECT_DIR, filePath)),
});
};

describe('project includes', () => {
it("doesn't error for matched files", () => {
expect(testParse('ts/included.ts')).not.toThrow();
expect(testParse('ts/included.tsx')).not.toThrow();
expect(testParse('js/included.js')).not.toThrow();
expect(testParse('js/included.jsx')).not.toThrow();
});

it('errors for not included files', () => {
expect(testParse('ts/notIncluded.ts')).toThrowErrorMatchingSnapshot();
expect(testParse('ts/notIncluded.tsx')).toThrowErrorMatchingSnapshot();
expect(testParse('js/notIncluded.js')).toThrowErrorMatchingSnapshot();
expect(testParse('js/notIncluded.jsx')).toThrowErrorMatchingSnapshot();
});
});

describe('"parserOptions.extraFileExtensions" is non-empty', () => {
describe('the extension matches', () => {
it('the file is included', () => {
expect(testParse('other/included.vue')).not.toThrow();
});

it("the file isn't included", () => {
expect(
testParse('other/notIncluded.vue'),
).toThrowErrorMatchingSnapshot();
});
});

it('the extension does not match', () => {
expect(
testParse('other/unknownFileType.unknown'),
).toThrowErrorMatchingSnapshot();
});
});
});
});

0 comments on commit ec21ee3

Please sign in to comment.