blob: 93294017f5cfce88bb824e346eed06a1110717d3 [file] [log] [blame]
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const semver_1 = __importDefault(require("semver"));
const ts = __importStar(require("typescript")); // leave this as * as ts so people using util package don't need syntheticDefaultImports
const ast_converter_1 = __importDefault(require("./ast-converter"));
const convert_1 = require("./convert");
const node_utils_1 = require("./node-utils");
const semantic_errors_1 = require("./semantic-errors");
const tsconfig_parser_1 = require("./tsconfig-parser");
/**
* This needs to be kept in sync with the top-level README.md in the
* typescript-eslint monorepo
*/
const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.2.1 <3.6.0';
const ACTIVE_TYPESCRIPT_VERSION = ts.version;
const isRunningSupportedTypeScriptVersion = semver_1.default.satisfies(ACTIVE_TYPESCRIPT_VERSION, SUPPORTED_TYPESCRIPT_VERSIONS);
let extra;
let warnedAboutTSVersion = false;
/**
* Compute the filename based on the parser options.
*
* Even if jsx option is set in typescript compiler, filename still has to
* contain .tsx file extension.
*
* @param options Parser options
*/
function getFileName({ jsx }) {
return jsx ? 'estree.tsx' : 'estree.ts';
}
/**
* Resets the extra config object
*/
function resetExtra() {
extra = {
tokens: null,
range: false,
loc: false,
comment: false,
comments: [],
strict: false,
jsx: false,
useJSXTextNode: false,
log: console.log,
projects: [],
errorOnUnknownASTType: false,
errorOnTypeScriptSyntacticAndSemanticIssues: false,
code: '',
tsconfigRootDir: process.cwd(),
extraFileExtensions: [],
preserveNodeMaps: undefined,
};
}
/**
* @param code The code of the file being linted
* @param options The config object
* @returns If found, returns the source file corresponding to the code and the containing program
*/
function getASTFromProject(code, options) {
return node_utils_1.firstDefined(tsconfig_parser_1.calculateProjectParserOptions(code, options.filePath || getFileName(options), extra), currentProgram => {
const ast = currentProgram.getSourceFile(options.filePath || getFileName(options));
return ast && { ast, program: currentProgram };
});
}
/**
* @param code The code of the file being linted
* @param options The config object
* @returns If found, returns the source file corresponding to the code and the containing program
*/
function getASTAndDefaultProject(code, options) {
const fileName = options.filePath || getFileName(options);
const program = tsconfig_parser_1.createProgram(code, fileName, extra);
const ast = program && program.getSourceFile(fileName);
return ast && { ast, program };
}
/**
* @param code The code of the file being linted
* @returns Returns a new source file and program corresponding to the linted code
*/
function createNewProgram(code) {
const FILENAME = getFileName(extra);
const compilerHost = {
fileExists() {
return true;
},
getCanonicalFileName() {
return FILENAME;
},
getCurrentDirectory() {
return '';
},
getDirectories() {
return [];
},
getDefaultLibFileName() {
return 'lib.d.ts';
},
// TODO: Support Windows CRLF
getNewLine() {
return '\n';
},
getSourceFile(filename) {
return ts.createSourceFile(filename, code, ts.ScriptTarget.Latest, true);
},
readFile() {
return undefined;
},
useCaseSensitiveFileNames() {
return true;
},
writeFile() {
return null;
},
};
const program = ts.createProgram([FILENAME], {
noResolve: true,
target: ts.ScriptTarget.Latest,
jsx: extra.jsx ? ts.JsxEmit.Preserve : undefined,
}, compilerHost);
const ast = program.getSourceFile(FILENAME);
return { ast, program };
}
/**
* @param code The code of the file being linted
* @param options The config object
* @param shouldProvideParserServices True iff the program should be attempted to be calculated from provided tsconfig files
* @returns Returns a source file and program corresponding to the linted code
*/
function getProgramAndAST(code, options, shouldProvideParserServices) {
return ((shouldProvideParserServices && getASTFromProject(code, options)) ||
(shouldProvideParserServices && getASTAndDefaultProject(code, options)) ||
createNewProgram(code));
}
function applyParserOptionsToExtra(options) {
/**
* Track range information in the AST
*/
extra.range = typeof options.range === 'boolean' && options.range;
extra.loc = typeof options.loc === 'boolean' && options.loc;
/**
* Track tokens in the AST
*/
if (typeof options.tokens === 'boolean' && options.tokens) {
extra.tokens = [];
}
/**
* Track comments in the AST
*/
if (typeof options.comment === 'boolean' && options.comment) {
extra.comment = true;
extra.comments = [];
}
/**
* Enable JSX - note the applicable file extension is still required
*/
if (typeof options.jsx === 'boolean' && options.jsx) {
extra.jsx = true;
}
/**
* The JSX AST changed the node type for string literals
* inside a JSX Element from `Literal` to `JSXText`.
*
* When value is `true`, these nodes will be parsed as type `JSXText`.
* When value is `false`, these nodes will be parsed as type `Literal`.
*/
if (typeof options.useJSXTextNode === 'boolean' && options.useJSXTextNode) {
extra.useJSXTextNode = true;
}
/**
* Allow the user to cause the parser to error if it encounters an unknown AST Node Type
* (used in testing)
*/
if (typeof options.errorOnUnknownASTType === 'boolean' &&
options.errorOnUnknownASTType) {
extra.errorOnUnknownASTType = true;
}
/**
* Allow the user to override the function used for logging
*/
if (typeof options.loggerFn === 'function') {
extra.log = options.loggerFn;
}
else if (options.loggerFn === false) {
extra.log = Function.prototype;
}
if (typeof options.project === 'string') {
extra.projects = [options.project];
}
else if (Array.isArray(options.project) &&
options.project.every(projectPath => typeof projectPath === 'string')) {
extra.projects = options.project;
}
if (typeof options.tsconfigRootDir === 'string') {
extra.tsconfigRootDir = options.tsconfigRootDir;
}
if (Array.isArray(options.extraFileExtensions) &&
options.extraFileExtensions.every(ext => typeof ext === 'string')) {
extra.extraFileExtensions = options.extraFileExtensions;
}
/**
* Allow the user to enable or disable the preservation of the AST node maps
* during the conversion process.
*
* NOTE: For backwards compatibility we also preserve node maps in the case where `project` is set,
* and `preserveNodeMaps` is not explicitly set to anything.
*/
extra.preserveNodeMaps =
typeof options.preserveNodeMaps === 'boolean' && options.preserveNodeMaps;
if (options.preserveNodeMaps === undefined && extra.projects.length > 0) {
extra.preserveNodeMaps = true;
}
}
function warnAboutTSVersion() {
if (!isRunningSupportedTypeScriptVersion && !warnedAboutTSVersion) {
const border = '=============';
const versionWarning = [
border,
'WARNING: You are currently running a version of TypeScript which is not officially supported by typescript-estree.',
'You may find that it works just fine, or you may not.',
`SUPPORTED TYPESCRIPT VERSIONS: ${SUPPORTED_TYPESCRIPT_VERSIONS}`,
`YOUR TYPESCRIPT VERSION: ${ACTIVE_TYPESCRIPT_VERSION}`,
'Please only submit bug reports when using the officially supported version.',
border,
];
extra.log(versionWarning.join('\n\n'));
warnedAboutTSVersion = true;
}
}
//------------------------------------------------------------------------------
// Public
//------------------------------------------------------------------------------
exports.version = require('../package.json').version;
function parse(code, options) {
/**
* Reset the parse configuration
*/
resetExtra();
/**
* Ensure users do not attempt to use parse() when they need parseAndGenerateServices()
*/
if (options && options.errorOnTypeScriptSyntacticAndSemanticIssues) {
throw new Error(`"errorOnTypeScriptSyntacticAndSemanticIssues" is only supported for parseAndGenerateServices()`);
}
/**
* Ensure the source code is a string, and store a reference to it
*/
if (typeof code !== 'string' && !(code instanceof String)) {
code = String(code);
}
extra.code = code;
/**
* Apply the given parser options
*/
if (typeof options !== 'undefined') {
applyParserOptionsToExtra(options);
}
/**
* Warn if the user is using an unsupported version of TypeScript
*/
warnAboutTSVersion();
/**
* Create a ts.SourceFile directly, no ts.Program is needed for a simple
* parse
*/
const ast = ts.createSourceFile(getFileName(extra), code, ts.ScriptTarget.Latest,
/* setParentNodes */ true);
/**
* Convert the TypeScript AST to an ESTree-compatible one
*/
const { estree } = ast_converter_1.default(ast, extra, false);
return estree;
}
exports.parse = parse;
function parseAndGenerateServices(code, options) {
/**
* Reset the parse configuration
*/
resetExtra();
/**
* Ensure the source code is a string, and store a reference to it
*/
if (typeof code !== 'string' && !(code instanceof String)) {
code = String(code);
}
extra.code = code;
/**
* Apply the given parser options
*/
if (typeof options !== 'undefined') {
applyParserOptionsToExtra(options);
if (typeof options.errorOnTypeScriptSyntacticAndSemanticIssues ===
'boolean' &&
options.errorOnTypeScriptSyntacticAndSemanticIssues) {
extra.errorOnTypeScriptSyntacticAndSemanticIssues = true;
}
}
/**
* Warn if the user is using an unsupported version of TypeScript
*/
warnAboutTSVersion();
/**
* Generate a full ts.Program in order to be able to provide parser
* services, such as type-checking
*/
const shouldProvideParserServices = extra.projects && extra.projects.length > 0;
const { ast, program } = getProgramAndAST(code, options, shouldProvideParserServices);
/**
* Determine whether or not two-way maps of converted AST nodes should be preserved
* during the conversion process
*/
const shouldPreserveNodeMaps = extra.preserveNodeMaps !== undefined
? extra.preserveNodeMaps
: shouldProvideParserServices;
/**
* Convert the TypeScript AST to an ESTree-compatible one, and optionally preserve
* mappings between converted and original AST nodes
*/
const { estree, astMaps } = ast_converter_1.default(ast, extra, shouldPreserveNodeMaps);
/**
* Even if TypeScript parsed the source code ok, and we had no problems converting the AST,
* there may be other syntactic or semantic issues in the code that we can optionally report on.
*/
if (program && extra.errorOnTypeScriptSyntacticAndSemanticIssues) {
const error = semantic_errors_1.getFirstSemanticOrSyntacticError(program, ast);
if (error) {
throw convert_1.convertError(error);
}
}
/**
* Return the converted AST and additional parser services
*/
return {
ast: estree,
services: {
program: shouldProvideParserServices ? program : undefined,
esTreeNodeToTSNodeMap: shouldPreserveNodeMaps && astMaps
? astMaps.esTreeNodeToTSNodeMap
: undefined,
tsNodeToESTreeNodeMap: shouldPreserveNodeMaps && astMaps
? astMaps.tsNodeToESTreeNodeMap
: undefined,
},
};
}
exports.parseAndGenerateServices = parseAndGenerateServices;
__export(require("./ts-estree"));
//# sourceMappingURL=parser.js.map