diff --git a/lib/helpers.js b/lib/helpers.js index 6ba063f2..49b06bbc 100644 --- a/lib/helpers.js +++ b/lib/helpers.js @@ -1,8 +1,11 @@ 'use babel'; +import { dirname } from 'path'; import stylelint from 'stylelint'; +import assignDeep from 'assign-deep'; import escapeHTML from 'escape-html'; import { generateRange } from 'atom-linter'; +import presetConfig from 'stylelint-config-standard'; export function startMeasure(baseName) { const markName = `${baseName}-start`; @@ -164,3 +167,34 @@ export const runStylelint = async (editor, stylelintOptions, filePath, settings) } return parseResults(editor, results, filePath, settings.showIgnored); }; + +export function getDefaultConfig(syntax, filePath) { + const defaultConfig = assignDeep({}, presetConfig); + + if (syntax === 'sugarss') { + // `stylelint-config-standard` isn't fully compatible with SugarSS + // See here for details: + // https://github.com/stylelint/stylelint-config-standard#using-the-config-with-sugarss-syntax + defaultConfig.rules['block-closing-brace-empty-line-before'] = null; + defaultConfig.rules['block-closing-brace-newline-after'] = null; + defaultConfig.rules['block-closing-brace-newline-before'] = null; + defaultConfig.rules['block-closing-brace-space-before'] = null; + defaultConfig.rules['block-opening-brace-newline-after'] = null; + defaultConfig.rules['block-opening-brace-space-after'] = null; + defaultConfig.rules['block-opening-brace-space-before'] = null; + defaultConfig.rules['declaration-block-semicolon-newline-after'] = null; + defaultConfig.rules['declaration-block-semicolon-space-after'] = null; + defaultConfig.rules['declaration-block-semicolon-space-before'] = null; + defaultConfig.rules['declaration-block-trailing-semicolon'] = null; + } + + // Base the config in the project directory + let [configBasedir] = atom.project.relativizePath(filePath); + if (configBasedir === null) { + // Falling back to the file directory if no project is found + configBasedir = dirname(filePath); + } + defaultConfig.configBasedir = configBasedir; + + return defaultConfig; +} diff --git a/lib/index.js b/lib/index.js index cfe791d4..ef599d1c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -8,7 +8,6 @@ let helpers; let dirname; let stylelint; let assignDeep; -let presetConfig; function loadDeps() { if (!helpers) { @@ -23,9 +22,6 @@ function loadDeps() { if (!assignDeep) { assignDeep = require('assign-deep'); } - if (!presetConfig) { - presetConfig = require('stylelint-config-standard'); - } } export default { @@ -44,11 +40,21 @@ export default { this.subscriptions = new CompositeDisposable(); this.subscriptions.add( - atom.config.observe('linter-stylelint.useStandard', (value) => { - this.useStandard = value; - }), atom.config.observe('linter-stylelint.disableWhenNoConfig', (value) => { this.disableWhenNoConfig = value; + if (this.useStandard && this.disableWhenNoConfig) { + // Disable using the standard if it is desired to stop linting with + // no configuration + atom.config.set('linter-stylelint.useStandard', false); + } + }), + atom.config.observe('linter-stylelint.useStandard', (value) => { + this.useStandard = value; + if (this.useStandard && this.disableWhenNoConfig) { + // Disable disabling linting when there is no configuration as the + // standard configuration will always be available. + atom.config.set('linter-stylelint.disableWhenNoConfig', false); + } }), atom.config.observe('linter-stylelint.showIgnored', (value) => { this.showIgnored = value; @@ -86,7 +92,6 @@ export default { loadDeps(); helpers.startMeasure('linter-stylelint: Lint'); - const scopes = editor.getLastCursor().getScopeDescriptor().getScopesArray(); const filePath = editor.getPath(); const text = editor.getText(); @@ -96,55 +101,26 @@ export default { return []; } - // Setup base config if useStandard() is true - const defaultConfig = { - rules: {} - }; - - // Base the config in the project directory - let [configBasedir] = atom.project.relativizePath(filePath); - if (configBasedir === null) { - // Falling back to the file directory if no project is found - configBasedir = dirname(filePath); - } - - const rules = this.useStandard ? assignDeep({}, presetConfig) : defaultConfig; - + const rules = {}; const options = { code: text, codeFilename: filePath, - config: rules, - configBasedir + config: { rules } }; - if (this.coreIgnored) { - // When Atom (and thus Linter) is set to allow ignored files, tell - // Stylelint to do the same. - options.disableDefaultIgnores = true; - } - + const scopes = editor.getLastCursor().getScopeDescriptor().getScopesArray(); if (scopes.includes('source.css.scss') || scopes.includes('source.scss')) { options.syntax = 'scss'; - } - if (scopes.includes('source.css.less') || scopes.includes('source.less')) { + } else if (scopes.includes('source.css.less') || scopes.includes('source.less')) { options.syntax = 'less'; - } - if (scopes.includes('source.css.postcss.sugarss')) { + } else if (scopes.includes('source.css.postcss.sugarss')) { options.syntax = 'sugarss'; - // `stylelint-config-standard` isn't fully compatible with SugarSS - // See here for details: - // https://github.com/stylelint/stylelint-config-standard#using-the-config-with-sugarss-syntax - options.config.rules['block-closing-brace-empty-line-before'] = null; - options.config.rules['block-closing-brace-newline-after'] = null; - options.config.rules['block-closing-brace-newline-before'] = null; - options.config.rules['block-closing-brace-space-before'] = null; - options.config.rules['block-opening-brace-newline-after'] = null; - options.config.rules['block-opening-brace-space-after'] = null; - options.config.rules['block-opening-brace-space-before'] = null; - options.config.rules['declaration-block-semicolon-newline-after'] = null; - options.config.rules['declaration-block-semicolon-space-after'] = null; - options.config.rules['declaration-block-semicolon-space-before'] = null; - options.config.rules['declaration-block-trailing-semicolon'] = null; + } + + if (this.coreIgnored) { + // When Atom (and thus Linter) is set to allow ignored files, tell + // Stylelint to do the same. + options.disableDefaultIgnores = true; } helpers.startMeasure('linter-stylelint: Create Linter'); @@ -172,13 +148,21 @@ export default { helpers.endMeasure('linter-stylelint: Config'); if (foundConfig) { + // We have a configuration from Stylelint options.config = assignDeep(rules, foundConfig.config); options.configBasedir = dirname(foundConfig.filepath); - } - - if (!foundConfig && this.disableWhenNoConfig) { + } else if (this.disableWhenNoConfig) { + // No configuration, and linting without one is disabled helpers.endMeasure('linter-stylelint: Lint'); return []; + } else if (this.useStandard) { + // No configuration, but using the standard is enabled + const defaultConfig = helpers.getDefaultConfig(options.syntax, filePath); + assignDeep(rules, defaultConfig.rules); + if (defaultConfig.extends) { + options.config.extends = defaultConfig.extends; + } + options.configBasedir = defaultConfig.configBasedir; } helpers.startMeasure('linter-stylelint: Check ignored'); diff --git a/package.json b/package.json index 0a11e7c5..1275f521 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "configSchema": { "useStandard": { "title": "Use standard", - "description": "Use the stylelint-config-standard lint configuration", + "description": "Use the stylelint-config-standard lint configuration when no other configuration is found. Disables the \"Disable when no config\" option.", "type": "boolean", "default": false }, diff --git a/spec/linter-stylelint-spec.js b/spec/linter-stylelint-spec.js index e46e4971..b9cfde9a 100644 --- a/spec/linter-stylelint-spec.js +++ b/spec/linter-stylelint-spec.js @@ -143,6 +143,10 @@ describe('The stylelint provider for Linter', () => { }); describe('ignores files when files are specified in ignoreFiles and', () => { + beforeEach(() => { + atom.config.set('linter-stylelint.useStandard', true); + }); + it('shows a message when asked to', async () => { atom.config.set('linter-stylelint.showIgnored', true); const editor = await atom.workspace.open(ignorePath); @@ -166,6 +170,7 @@ describe('The stylelint provider for Linter', () => { }); it("doesn't persist settings across runs", async () => { + atom.config.set('linter-stylelint.useStandard', true); atom.config.set('linter-stylelint.disableWhenNoConfig', false); // The config for this folder breaks the block-no-empty rule const invalidEditor = await atom.workspace.open(invalidRulePath); @@ -187,6 +192,7 @@ describe('The stylelint provider for Linter', () => { describe('works with Less files and', () => { beforeEach(async () => { + atom.config.set('linter-stylelint.useStandard', true); atom.config.set('linter-stylelint.disableWhenNoConfig', false); await atom.packages.activatePackage('language-less'); }); @@ -214,6 +220,7 @@ describe('The stylelint provider for Linter', () => { describe('works with PostCSS files and', () => { beforeEach(async () => { + atom.config.set('linter-stylelint.useStandard', true); atom.config.set('linter-stylelint.disableWhenNoConfig', false); await atom.packages.activatePackage('language-postcss'); }); @@ -241,6 +248,7 @@ describe('The stylelint provider for Linter', () => { describe('works with SugarSS files and', () => { beforeEach(async () => { + atom.config.set('linter-stylelint.useStandard', true); atom.config.set('linter-stylelint.disableWhenNoConfig', false); await atom.packages.activatePackage('language-postcss'); });