diff --git a/lib/is-config-at-home-root.js b/lib/is-config-at-home-root.js new file mode 100644 index 00000000..915f4225 --- /dev/null +++ b/lib/is-config-at-home-root.js @@ -0,0 +1,28 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.isConfigAtHomeRoot = isConfigAtHomeRoot; + +var _userHome = require('user-home'); + +var _userHome2 = _interopRequireDefault(_userHome); + +var _path = require('path'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Check if a config is directly inside a user's home directory. + * Such config files are used by ESLint as a fallback, only for situations + * when there is no other config file between a file being linted and root. + * + * @param {string} configPath - The path of the config file being checked + * @return {Boolean} True if the file is directly in the current user's home + */ +/* eslint-disable import/prefer-default-export */ + +function isConfigAtHomeRoot(configPath) { + return (0, _path.dirname)(configPath) === _userHome2.default; +} \ No newline at end of file diff --git a/lib/main.js b/lib/main.js index 25e1cfaa..81b27be8 100644 --- a/lib/main.js +++ b/lib/main.js @@ -11,6 +11,8 @@ var _helpers = require('./helpers'); var _workerHelpers = require('./worker-helpers'); +var _isConfigAtHomeRoot = require('./is-config-at-home-root'); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } @@ -58,7 +60,8 @@ module.exports = { // Do not try to fix if linting should be disabled const fileDir = _path2.default.dirname(filePath); const configPath = (0, _workerHelpers.getConfigPath)(fileDir); - if (configPath === null && disableWhenNoEslintConfig) return; + const noProjectConfig = configPath === null || (0, _isConfigAtHomeRoot.isConfigAtHomeRoot)(configPath); + if (noProjectConfig && disableWhenNoEslintConfig) return; this.worker.request('job', { type: 'fix', diff --git a/lib/worker.js b/lib/worker.js index 0083c555..8c65c4e8 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -15,6 +15,8 @@ var _workerHelpers = require('./worker-helpers'); var Helpers = _interopRequireWildcard(_workerHelpers); +var _isConfigAtHomeRoot = require('./is-config-at-home-root'); + function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } @@ -32,7 +34,8 @@ const ignoredMessages = [ 'File ignored by default. Use a negated ignore pattern (like "--ignore-pattern \'!\'") to override.', 'File ignored by default. Use "--ignore-pattern \'!node_modules/*\'" to override.', 'File ignored by default. Use "--ignore-pattern \'!bower_components/*\'" to override.']; function lintJob(argv, contents, eslint, configPath, config) { - if (configPath === null && config.disableWhenNoEslintConfig) { + const noProjectConfig = configPath === null || (0, _isConfigAtHomeRoot.isConfigAtHomeRoot)(configPath); + if (noProjectConfig && config.disableWhenNoEslintConfig) { return []; } eslint.execute(argv, contents); diff --git a/package.json b/package.json index 489e4475..ff37e879 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,8 @@ "eslint": "^3.6.0", "eslint-rule-documentation": "^1.0.0", "process-communication": "^1.1.0", - "resolve-env": "^1.0.0" + "resolve-env": "^1.0.0", + "user-home": "^2.0.0" }, "devDependencies": { "babel-cli": "^6.18.0", diff --git a/spec/fixtures/files/badInline.js b/spec/fixtures/files/badInline.js new file mode 100644 index 00000000..1c67f73a --- /dev/null +++ b/spec/fixtures/files/badInline.js @@ -0,0 +1,3 @@ +/* eslint no-undef: error */ + +foo = 42; diff --git a/spec/linter-eslint-spec.js b/spec/linter-eslint-spec.js index 3c6be10e..0be4cf2b 100644 --- a/spec/linter-eslint-spec.js +++ b/spec/linter-eslint-spec.js @@ -10,6 +10,7 @@ const fixturesDir = path.join(__dirname, 'fixtures') const goodPath = path.join(fixturesDir, 'files', 'good.js') const badPath = path.join(fixturesDir, 'files', 'bad.js') +const badInlinePath = path.join(fixturesDir, 'files', 'badInline.js') const emptyPath = path.join(fixturesDir, 'files', 'empty.js') const fixPath = path.join(fixturesDir, 'files', 'fix.js') const configPath = path.join(fixturesDir, 'configs', '.eslintrc.yml') @@ -24,6 +25,20 @@ const modifiedIgnoreSpacePath = path.join(fixturesDir, 'modified-ignore-rule', 'foo-space.js') const endRangePath = path.join(fixturesDir, 'end-range', 'no-unreachable.js') +function copyFileToTempDir(fileToCopyPath) { + return new Promise((resolve) => { + const tempFixtureDir = fs.mkdtempSync(tmpdir() + path.sep) + const tempFixturePath = path.join(tempFixtureDir, path.basename(fileToCopyPath)) + const wr = fs.createWriteStream(tempFixturePath) + wr.on('close', () => + atom.workspace.open(tempFixturePath).then((openEditor) => { + resolve({ openEditor, tempDir: tempFixtureDir }) + }) + ) + fs.createReadStream(fileToCopyPath).pipe(wr) + }) +} + describe('The eslint provider for Linter', () => { const { spawnWorker } = require('../lib/helpers') @@ -361,4 +376,80 @@ describe('The eslint provider for Linter', () => { ) ) ) + + describe('when setting `disableWhenNoEslintConfig` is false', () => { + let editor + let didError + let gotLintingErrors + let tempFixtureDir + + beforeEach(() => { + atom.config.set('linter-eslint.disableWhenNoEslintConfig', false) + + waitsForPromise(() => + copyFileToTempDir(badInlinePath, tempFixtureDir) + .then(({ openEditor, tempDir }) => { + editor = openEditor + tempFixtureDir = tempDir + }) + ) + }) + + afterEach(() => { + rimraf.sync(tempFixtureDir) + }) + + it('errors when no config file is found', () => { + lint(editor) + .then((messages) => { + // Older versions of ESLint will report an error + // (or if current user running tests has a config in their home directory) + const expectedHtml = 'no-undef 'foo' is not defined.' + expect(messages.length).toBe(1) + expect(messages[0].html).toBe(expectedHtml) + gotLintingErrors = true + }) + .catch((err) => { + // Newer versions of ESLint will throw an exception + expect(err.message).toBe('No ESLint configuration found.') + didError = true + }) + + waitsFor( + () => didError || gotLintingErrors, + 'An error should have been thrown or linting performed' + ) + }) + }) + + describe('when `disableWhenNoEslintConfig` is true', () => { + let editor + let tempFixtureDir + + beforeEach(() => { + atom.config.set('linter-eslint.disableWhenNoEslintConfig', true) + + waitsForPromise(() => + copyFileToTempDir(badInlinePath) + .then(({ openEditor, tempDir }) => { + editor = openEditor + tempFixtureDir = tempDir + }) + ) + }) + + afterEach(() => { + rimraf.sync(tempFixtureDir) + }) + + it('does not report errors when no config file is found', () => + waitsForPromise(() => + lint(editor) + .then((messages) => { + expect(messages.length).toBe(0) + }) + ) + ) + }) }) diff --git a/src/is-config-at-home-root.js b/src/is-config-at-home-root.js new file mode 100644 index 00000000..74c16446 --- /dev/null +++ b/src/is-config-at-home-root.js @@ -0,0 +1,16 @@ +/* eslint-disable import/prefer-default-export */ + +import userHome from 'user-home' +import { dirname } from 'path' + +/** + * Check if a config is directly inside a user's home directory. + * Such config files are used by ESLint as a fallback, only for situations + * when there is no other config file between a file being linted and root. + * + * @param {string} configPath - The path of the config file being checked + * @return {Boolean} True if the file is directly in the current user's home + */ +export function isConfigAtHomeRoot(configPath) { + return (dirname(configPath) === userHome) +} diff --git a/src/main.js b/src/main.js index f3269fc3..a0638390 100644 --- a/src/main.js +++ b/src/main.js @@ -6,9 +6,10 @@ import { CompositeDisposable, } from 'atom' import { spawnWorker, showError, idsToIgnoredRules, processESLintMessages, - generateDebugString + generateDebugString, } from './helpers' import { getConfigPath } from './worker-helpers' +import { isConfigAtHomeRoot } from './is-config-at-home-root' // Configuration const scopes = [] @@ -54,7 +55,8 @@ module.exports = { // Do not try to fix if linting should be disabled const fileDir = Path.dirname(filePath) const configPath = getConfigPath(fileDir) - if (configPath === null && disableWhenNoEslintConfig) return + const noProjectConfig = (configPath === null || isConfigAtHomeRoot(configPath)) + if (noProjectConfig && disableWhenNoEslintConfig) return this.worker.request('job', { type: 'fix', diff --git a/src/worker.js b/src/worker.js index fe6f7107..01b9b001 100644 --- a/src/worker.js +++ b/src/worker.js @@ -6,6 +6,7 @@ import Path from 'path' import { create } from 'process-communication' import { FindCache, findCached } from 'atom-linter' import * as Helpers from './worker-helpers' +import { isConfigAtHomeRoot } from './is-config-at-home-root' process.title = 'linter-eslint helper' @@ -24,7 +25,8 @@ const ignoredMessages = [ ] function lintJob(argv, contents, eslint, configPath, config) { - if (configPath === null && config.disableWhenNoEslintConfig) { + const noProjectConfig = (configPath === null || isConfigAtHomeRoot(configPath)) + if (noProjectConfig && config.disableWhenNoEslintConfig) { return [] } eslint.execute(argv, contents)