From aec339a8476fb8d73bb65b2b8a73384ab92f980b Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Sat, 18 Jun 2016 12:21:50 -0700 Subject: [PATCH] Fix: Ensure config extends reads from the right spot (fixes #5450) --- lib/config/config-file.js | 37 ++++++++++++++++--- .../extends-chain-2/relative.eslintrc.json | 3 ++ tests/lib/config/config-file.js | 17 ++++++++- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 tests/fixtures/config-file/extends-chain-2/relative.eslintrc.json diff --git a/lib/config/config-file.js b/lib/config/config-file.js index e68591874c0b..7184e3193d2b 100644 --- a/lib/config/config-file.js +++ b/lib/config/config-file.js @@ -377,18 +377,35 @@ function applyExtends(config, filePath, relativeTo) { parentPath = path.resolve(__dirname, "../../conf/eslint-all.js"); } else if (isFilePath(parentPath)) { + debug("parentPath before:" + parentPath); + /* * If the `extends` path is relative, use the directory of the current configuration * file as the reference point. Otherwise, use as-is. */ - parentPath = (!isAbsolutePath(parentPath) ? - path.join(relativeTo || path.dirname(filePath), parentPath) : - parentPath - ); + if (!isAbsolutePath(parentPath)) { + var dirname = path.dirname(filePath); + + /* + * Special case: if filePath is a package name (such as "foo" for eslint-config-foo) + * then dirname will end up as just ".". When you pass "." into path.join(), it is + * treated differently so if parentPath is "./foo.js" it becomes "foo.js". When that + * happens, the rest of the logic breaks because "foo.js" looks like a package name. + * So, we have to just not do the join in that situation. See: + * https://github.com/eslint/eslint/issues/6450 + * https://github.com/eslint/eslint/issues/6358 + */ + if (dirname !== ".") { + parentPath = path.join(dirname, parentPath); + } + + } + + debug("parentPath after:" + parentPath); } try { - debug("Loading " + parentPath); + debug("Loading " + parentPath + " relative to " + relativeTo); return ConfigOps.merge(load(parentPath, false, relativeTo), previousValue); } catch (e) { @@ -460,11 +477,16 @@ function normalizePackageName(name, prefix) { * @private */ function resolve(filePath, relativeTo) { + debug("resolve(): " + filePath + "," + relativeTo); + if (isFilePath(filePath)) { + debug("Path to resolve is a file path:" + filePath); return { filePath: path.resolve(relativeTo || "", filePath) }; } else { var normalizedPackageName; + debug("filePath " + filePath + " is not a filepath, must be a package name"); + if (filePath.indexOf("plugin:") === 0) { var packagePath = filePath.substr(7, filePath.lastIndexOf("/") - 7); var configName = filePath.substr(filePath.lastIndexOf("/") + 1, filePath.length - filePath.lastIndexOf("/") - 1); @@ -493,12 +515,17 @@ function resolve(filePath, relativeTo) { * @private */ function load(filePath, applyEnvironments, relativeTo) { + + debug("load(): " + filePath + ", " + relativeTo); + var resolvedPath = resolve(filePath, relativeTo), dirname = path.dirname(resolvedPath.filePath), basedir = getBaseDir(dirname), lookupPath = getLookupPath(dirname), config = loadConfigFile(resolvedPath); + debug("load(): Resolved path is " + JSON.stringify(resolvedPath)); + if (config) { // ensure plugins are properly loaded first diff --git a/tests/fixtures/config-file/extends-chain-2/relative.eslintrc.json b/tests/fixtures/config-file/extends-chain-2/relative.eslintrc.json new file mode 100644 index 000000000000..ad711c30b2ce --- /dev/null +++ b/tests/fixtures/config-file/extends-chain-2/relative.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "./node_modules/eslint-config-a/index.js" +} diff --git a/tests/lib/config/config-file.js b/tests/lib/config/config-file.js index bfb80f562c86..25b738726356 100644 --- a/tests/lib/config/config-file.js +++ b/tests/lib/config/config-file.js @@ -642,7 +642,7 @@ describe("ConfigFile", function() { }); }); - it("should load information from `extends` chain with relative path.", function() { + it("should load information from `extends` chain in .eslintrc with relative path.", function() { var config = ConfigFile.load(getFixturePath("extends-chain-2/.eslintrc.json")); assert.deepEqual(config, { @@ -657,6 +657,21 @@ describe("ConfigFile", function() { }); }); + it("should load information from `extends` chain in non-.eslintrc file with relative path.", function() { + var config = ConfigFile.load(getFixturePath("extends-chain-2/relative.eslintrc.json")); + + assert.deepEqual(config, { + env: {}, + extends: "./node_modules/eslint-config-a/index.js", + globals: {}, + parserOptions: {}, + rules: { + a: 2, // from node_modules/eslint-config-a/index.js + relative: 2 // from node_modules/eslint-config-a/relative.js + } + }); + }); + describe("Plugins", function() { it("should load information from a YML file and load plugins", function() {