blob: 20db62122443d03133db06bdab1f95b098469800 [file] [log] [blame]
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseAndGenerateServices = exports.parse = void 0;
const debug_1 = __importDefault(require("debug"));
const globby_1 = require("globby");
const is_glob_1 = __importDefault(require("is-glob"));
const semver_1 = __importDefault(require("semver"));
const ts = __importStar(require("typescript"));
const ast_converter_1 = require("./ast-converter");
const convert_1 = require("./convert");
const createDefaultProgram_1 = require("./create-program/createDefaultProgram");
const createIsolatedProgram_1 = require("./create-program/createIsolatedProgram");
const createProjectProgram_1 = require("./create-program/createProjectProgram");
const createSourceFile_1 = require("./create-program/createSourceFile");
const semantic_or_syntactic_errors_1 = require("./semantic-or-syntactic-errors");
const shared_1 = require("./create-program/shared");
const log = debug_1.default('typescript-eslint:typescript-estree:parser');
/**
* This needs to be kept in sync with the top-level README.md in the
* typescript-eslint monorepo
*/
const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.3.1 <4.1.0';
/*
* The semver package will ignore prerelease ranges, and we don't want to explicitly document every one
* List them all separately here, so we can automatically create the full string
*/
const SUPPORTED_PRERELEASE_RANGES = [];
const ACTIVE_TYPESCRIPT_VERSION = ts.version;
const isRunningSupportedTypeScriptVersion = semver_1.default.satisfies(ACTIVE_TYPESCRIPT_VERSION, [SUPPORTED_TYPESCRIPT_VERSIONS]
.concat(SUPPORTED_PRERELEASE_RANGES)
.join(' || '));
let extra;
let warnedAboutTSVersion = false;
function enforceString(code) {
/**
* Ensure the source code is a string
*/
if (typeof code !== 'string') {
return String(code);
}
return code;
}
/**
* @param code The code of the file being linted
* @param shouldProvideParserServices True if the program should be attempted to be calculated from provided tsconfig files
* @param shouldCreateDefaultProgram True if the program should be created from compiler host
* @returns Returns a source file and program corresponding to the linted code
*/
function getProgramAndAST(code, shouldProvideParserServices, shouldCreateDefaultProgram) {
return ((shouldProvideParserServices &&
createProjectProgram_1.createProjectProgram(code, shouldCreateDefaultProgram, extra)) ||
(shouldProvideParserServices &&
shouldCreateDefaultProgram &&
createDefaultProgram_1.createDefaultProgram(code, extra)) ||
createIsolatedProgram_1.createIsolatedProgram(code, extra));
}
/**
* 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 = {
code: '',
comment: false,
comments: [],
createDefaultProgram: false,
debugLevel: new Set(),
errorOnTypeScriptSyntacticAndSemanticIssues: false,
errorOnUnknownASTType: false,
extraFileExtensions: [],
filePath: getFileName(),
jsx: false,
loc: false,
log: console.log,
preserveNodeMaps: true,
projects: [],
range: false,
strict: false,
tokens: null,
tsconfigRootDir: process.cwd(),
useJSXTextNode: false,
};
}
/**
* Normalizes, sanitizes, resolves and filters the provided
*/
function prepareAndTransformProjects(projectsInput, ignoreListInput) {
let projects = [];
// Normalize and sanitize the project paths
if (typeof projectsInput === 'string') {
projects.push(projectsInput);
}
else if (Array.isArray(projectsInput)) {
for (const project of projectsInput) {
if (typeof project === 'string') {
projects.push(project);
}
}
}
if (projects.length === 0) {
return projects;
}
// Transform glob patterns into paths
const globbedProjects = projects.filter(project => is_glob_1.default(project));
projects = projects
.filter(project => !is_glob_1.default(project))
.concat(globby_1.sync([...globbedProjects, ...ignoreListInput], {
cwd: extra.tsconfigRootDir,
}));
log('parserOptions.project (excluding ignored) matched projects: %s', projects);
return projects;
}
function applyParserOptionsToExtra(options) {
var _a;
/**
* Configure Debug logging
*/
if (options.debugLevel === true) {
extra.debugLevel = new Set(['typescript-eslint']);
}
else if (Array.isArray(options.debugLevel)) {
extra.debugLevel = new Set(options.debugLevel);
}
if (extra.debugLevel.size > 0) {
// debug doesn't support multiple `enable` calls, so have to do it all at once
const namespaces = [];
if (extra.debugLevel.has('typescript-eslint')) {
namespaces.push('typescript-eslint:*');
}
if (extra.debugLevel.has('eslint') ||
// make sure we don't turn off the eslint debug if it was enabled via --debug
debug_1.default.enabled('eslint:*')) {
// https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/bin/eslint.js#L25
namespaces.push('eslint:*,-eslint:code-path');
}
debug_1.default.enable(namespaces.join(','));
}
/**
* 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;
}
/**
* Get the file path
*/
if (typeof options.filePath === 'string' && options.filePath !== '<input>') {
extra.filePath = options.filePath;
}
else {
extra.filePath = getFileName(extra);
}
/**
* 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 = () => { };
}
if (typeof options.tsconfigRootDir === 'string') {
extra.tsconfigRootDir = options.tsconfigRootDir;
}
// NOTE - ensureAbsolutePath relies upon having the correct tsconfigRootDir in extra
extra.filePath = shared_1.ensureAbsolutePath(extra.filePath, extra);
// NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra
const projectFolderIgnoreList = ((_a = options.projectFolderIgnoreList) !== null && _a !== void 0 ? _a : [])
.reduce((acc, folder) => {
if (typeof folder === 'string') {
acc.push(folder);
}
return acc;
}, [])
// prefix with a ! for not match glob
.map(folder => (folder.startsWith('!') ? folder : `!${folder}`));
extra.projects = prepareAndTransformProjects(options.project, projectFolderIgnoreList);
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.
*/
if (typeof options.preserveNodeMaps === 'boolean') {
extra.preserveNodeMaps = options.preserveNodeMaps;
}
extra.createDefaultProgram =
typeof options.createDefaultProgram === 'boolean' &&
options.createDefaultProgram;
}
function warnAboutTSVersion() {
var _a;
if (!isRunningSupportedTypeScriptVersion && !warnedAboutTSVersion) {
const isTTY = typeof process === undefined ? false : (_a = process.stdout) === null || _a === void 0 ? void 0 : _a.isTTY;
if (isTTY) {
const border = '=============';
const versionWarning = [
border,
'WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/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;
}
}
function parse(code, options) {
/**
* Reset the parse configuration
*/
resetExtra();
/**
* Ensure users do not attempt to use parse() when they need parseAndGenerateServices()
*/
if (options === null || options === void 0 ? void 0 : options.errorOnTypeScriptSyntacticAndSemanticIssues) {
throw new Error(`"errorOnTypeScriptSyntacticAndSemanticIssues" is only supported for parseAndGenerateServices()`);
}
/**
* Ensure the source code is a string, and store a reference to it
*/
code = enforceString(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 = createSourceFile_1.createSourceFile(code, extra);
/**
* Convert the TypeScript AST to an ESTree-compatible one
*/
const { estree } = ast_converter_1.astConverter(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
*/
code = enforceString(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, shouldProvideParserServices, extra.createDefaultProgram);
/**
* Convert the TypeScript AST to an ESTree-compatible one, and optionally preserve
* mappings between converted and original AST nodes
*/
const preserveNodeMaps = typeof extra.preserveNodeMaps === 'boolean' ? extra.preserveNodeMaps : true;
const { estree, astMaps } = ast_converter_1.astConverter(ast, extra, preserveNodeMaps);
/**
* 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_or_syntactic_errors_1.getFirstSemanticOrSyntacticError(program, ast);
if (error) {
throw convert_1.convertError(error);
}
}
/**
* Return the converted AST and additional parser services
*/
return {
ast: estree,
services: {
hasFullTypeInformation: shouldProvideParserServices,
program,
esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap,
tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap,
},
};
}
exports.parseAndGenerateServices = parseAndGenerateServices;
//# sourceMappingURL=parser.js.map