eslintScope = require("eslint-scope"),
evk = require("eslint-visitor-keys"),
espree = require("espree"),
- lodash = require("lodash"),
+ merge = require("lodash.merge"),
BuiltInEnvironments = require("@eslint/eslintrc/conf/environments"),
pkg = require("../../package.json"),
astUtils = require("../shared/ast-utils"),
const debug = require("debug")("eslint:linter");
const MAX_AUTOFIX_PASSES = 10;
const DEFAULT_PARSER_NAME = "espree";
+const DEFAULT_ECMA_VERSION = 5;
const commentParser = new ConfigCommentParser();
const DEFAULT_ERROR_LOC = { start: { line: 1, column: 0 }, end: { line: 1, column: 1 } };
+const parserSymbol = Symbol.for("eslint.RuleTester.parser");
//------------------------------------------------------------------------------
// Typedefs
/**
* Normalize ECMAScript version from the initial config
- * @param {number} ecmaVersion ECMAScript version from the initial config
+ * @param {Parser} parser The parser which uses this options.
+ * @param {number} ecmaVersion ECMAScript version from the initial config
* @returns {number} normalized ECMAScript version
*/
-function normalizeEcmaVersion(ecmaVersion) {
+function normalizeEcmaVersion(parser, ecmaVersion) {
+ if ((parser[parserSymbol] || parser) === espree) {
+ if (ecmaVersion === "latest") {
+ return espree.latestEcmaVersion;
+ }
+ }
/*
* Calculate ECMAScript edition number from official year version starting with
return ecmaVersion >= 2015 ? ecmaVersion - 2009 : ecmaVersion;
}
-const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//gu;
+const eslintEnvPattern = /\/\*\s*eslint-env\s(.+?)\*\//gsu;
/**
* Checks whether or not there is a comment which has "eslint-env *" in a given text.
/**
* Combines the provided parserOptions with the options from environments
- * @param {string} parserName The parser name which uses this options.
+ * @param {Parser} parser The parser which uses this options.
* @param {ParserOptions} providedOptions The provided 'parserOptions' key in a config
* @param {Environment[]} enabledEnvironments The environments enabled in configuration and with inline comments
* @returns {ParserOptions} Resulting parser options after merge
*/
-function resolveParserOptions(parserName, providedOptions, enabledEnvironments) {
+function resolveParserOptions(parser, providedOptions, enabledEnvironments) {
+
const parserOptionsFromEnv = enabledEnvironments
.filter(env => env.parserOptions)
- .reduce((parserOptions, env) => lodash.merge(parserOptions, env.parserOptions), {});
- const mergedParserOptions = lodash.merge(parserOptionsFromEnv, providedOptions || {});
+ .reduce((parserOptions, env) => merge(parserOptions, env.parserOptions), {});
+ const mergedParserOptions = merge(parserOptionsFromEnv, providedOptions || {});
const isModule = mergedParserOptions.sourceType === "module";
if (isModule) {
mergedParserOptions.ecmaFeatures = Object.assign({}, mergedParserOptions.ecmaFeatures, { globalReturn: false });
}
- /*
- * TODO: @aladdin-add
- * 1. for a 3rd-party parser, do not normalize parserOptions
- * 2. for espree, no need to do this (espree will do it)
- */
- mergedParserOptions.ecmaVersion = normalizeEcmaVersion(mergedParserOptions.ecmaVersion);
+ mergedParserOptions.ecmaVersion = normalizeEcmaVersion(parser, mergedParserOptions.ecmaVersion);
return mergedParserOptions;
}
*/
function analyzeScope(ast, parserOptions, visitorKeys) {
const ecmaFeatures = parserOptions.ecmaFeatures || {};
- const ecmaVersion = parserOptions.ecmaVersion || 5;
+ const ecmaVersion = parserOptions.ecmaVersion || DEFAULT_ECMA_VERSION;
return eslintScope.analyze(ast, {
ignoreEval: true,
* @param {string} filename The reported filename of the code
* @param {boolean} disableFixes If true, it doesn't make `fix` properties.
* @param {string | undefined} cwd cwd of the cli
+ * @param {string} physicalFilename The full path of the file on disk without any code block information
* @returns {Problem[]} An array of reported problems
*/
-function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd) {
+function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd, physicalFilename) {
const emitter = createEmitter();
const nodeQueue = [];
let currentNode = sourceCode.ast;
getDeclaredVariables: sourceCode.scopeManager.getDeclaredVariables.bind(sourceCode.scopeManager),
getCwd: () => cwd,
getFilename: () => filename,
+ getPhysicalFilename: () => physicalFilename || filename,
getScope: () => getScope(sourceCode.scopeManager, currentNode),
getSourceCode: () => sourceCode,
markVariableAsUsed: name => markVariableAsUsed(sourceCode.scopeManager, currentNode, parserOptions, name),
.map(envName => getEnv(slots, envName))
.filter(env => env);
- const parserOptions = resolveParserOptions(parserName, config.parserOptions || {}, enabledEnvs);
+ const parserOptions = resolveParserOptions(parser, config.parserOptions || {}, enabledEnvs);
const configuredGlobals = resolveGlobals(config.globals || {}, enabledEnvs);
const settings = config.settings || {};
settings,
options.filename,
options.disableFixes,
- slots.cwd
+ slots.cwd,
+ providedOptions.physicalFilename
);
} catch (err) {
err.message += `\nOccurred while linting ${options.filename}`;
_verifyWithProcessor(textOrSourceCode, config, options, configForRecursive) {
const filename = options.filename || "<input>";
const filenameToExpose = normalizeFilename(filename);
+ const physicalFilename = options.physicalFilename || filenameToExpose;
const text = ensureText(textOrSourceCode);
const preprocess = options.preprocess || (rawText => [rawText]);
- const postprocess = options.postprocess || lodash.flatten;
+
+ // TODO(stephenwade): Replace this with array.flat() when we drop support for Node v10
+ const postprocess = options.postprocess || (array => [].concat(...array));
const filterCodeBlock =
options.filterCodeBlock ||
(blockFilename => blockFilename.endsWith(".js"));
return [];
}
- // Resolve configuration again if the file extension was changed.
- if (configForRecursive && path.extname(blockName) !== originalExtname) {
- debug("Resolving configuration again because the file extension was changed.");
+ // Resolve configuration again if the file content or extension was changed.
+ if (configForRecursive && (text !== blockText || path.extname(blockName) !== originalExtname)) {
+ debug("Resolving configuration again because the file content or extension was changed.");
return this._verifyWithConfigArray(
blockText,
configForRecursive,
- { ...options, filename: blockName }
+ { ...options, filename: blockName, physicalFilename }
);
}
return this._verifyWithoutProcessors(
blockText,
config,
- { ...options, filename: blockName }
+ { ...options, filename: blockName, physicalFilename }
);
});