diff --git a/src/helpers/get-config.ts b/src/helpers/get-config.ts index ee08fac4..96b591a9 100644 --- a/src/helpers/get-config.ts +++ b/src/helpers/get-config.ts @@ -1,17 +1,14 @@ import { Nullable, SlfParserOptions } from '@src/types'; +import * as fs from 'fs'; +import * as path from 'path'; const merge = require('merge'); -const path = require('path'); -const fs = require('fs'); const yaml = require('js-yaml'); type OptionParser = (filename: string) => Nullable; interface MappedParserOptions { [key: string]: OptionParser; } -interface JSONObject { - [key: string]: any; -} const _configurationProxy = new Proxy( { @@ -46,10 +43,10 @@ function parseModule(filename: string): Nullable { return require(path.resolve(filename)); } -export const getConfig = (filename: string): JSONObject => +export const getConfig = (filename: string): Record => _configurationProxy[filename]; export const mergeConfig = ( - baseConfig: JSONObject, - extendedConfig: JSONObject, + baseConfig: Record, + extendedConfig: Record, ) => merge.recursive(true, baseConfig, extendedConfig); diff --git a/src/sass-lint-auto-fix.ts b/src/sass-lint-auto-fix.ts index 7caea005..29c430e9 100755 --- a/src/sass-lint-auto-fix.ts +++ b/src/sass-lint-auto-fix.ts @@ -25,77 +25,84 @@ export function autoFixSassFactory(config: ConfigOpts) { const _slRules = config.slRules || slRules; const _slConfig = config.slConfig || slConfig; - const files = glob.sync(config.files.include, { - ignore: config.files.ignore, - }); + const patternsToInclude = + typeof config.files.include === 'string' + ? [config.files.include] + : config.files.include; return function* autoFixSass( options: LintOpts, ): IterableIterator { - for (const filename of files) { - const content = fs.readFileSync(filename).toString(); - if (content !== null && content !== undefined && content.length > 0) { - const fileExtension = path - .extname(filename) - .substr(1) - .toLowerCase(); - - if (isValidExtension(fileExtension)) { - let ast; - - try { - ast = parse(content, { - syntax: fileExtension, - }); - } catch (e) { - logger.warn('parse', filename, e); - return; - } - - const rules = _slRules(_slConfig(options)); - - const filteredRules: SlRule[] = rules.filter( - (rule: SlRule) => config.resolvers[rule.rule.name], - ); - - for (const rule of filteredRules) { - const { name } = rule.rule; - let resolver: Resolver; + for (const pattern of patternsToInclude) { + const files = glob.sync(pattern, { + ignore: config.files.ignore, + }); + + for (const filename of files) { + const content = fs.readFileSync(filename).toString(); + if (content !== null && content !== undefined && content.length > 0) { + const fileExtension = path + .extname(filename) + .substr(1) + .toLowerCase(); + + if (isValidExtension(fileExtension)) { + let ast; try { - resolver = createModule({ - name, - ast, - rule, + ast = parse(content, { + syntax: fileExtension, }); } catch (e) { - SentryService.reportIncident(e); - logger.warn('resolver', `Module '${name}' doesn't exist.`); + logger.warn('parse', filename, e); return; } - try { - const detects = rule.rule.detect(ast, rule); + const rules = _slRules(_slConfig(options)); - if (detects.length > 0) { - logger.verbose( - 'fix', - `Running resolver "${name}" on "${filename}"`, - ); + const filteredRules: SlRule[] = rules.filter( + (rule: SlRule) => config.resolvers[rule.rule.name], + ); - ast = resolver.fix(detects); - yield { + for (const rule of filteredRules) { + const { name } = rule.rule; + let resolver: Resolver; + + try { + resolver = createModule({ + name, ast, - filename, rule, - }; - } - } catch (e) { - if (!config.options.optOut) { + }); + } catch (e) { SentryService.reportIncident(e); + logger.warn('resolver', `Module '${name}' doesn't exist.`); + return; + } + + try { + const detects = rule.rule.detect(ast, rule); + + if (detects.length > 0) { + logger.verbose( + 'fix', + `Running resolver "${name}" on "${filename}"`, + ); + + ast = resolver.fix(detects); + yield { + ast, + filename, + rule, + }; + } + } catch (e) { + if (!config.options.optOut) { + SentryService.reportIncident(e); + } + // TODO: Friendly way to inform user that an unexpected error occured + logger.warn('error', e); } - // TODO: Friendly way to inform user that an unexpected error occured - logger.warn('error', e); } } } diff --git a/src/types/base.ts b/src/types/base.ts index 27f69662..dc16046a 100644 --- a/src/types/base.ts +++ b/src/types/base.ts @@ -15,7 +15,7 @@ export interface SlfParserOptions { } export interface SlfParserSyntaxOptions { - include: string[]; + include: (keyof typeof ValidFileType)[]; } export interface Resolution { @@ -29,7 +29,7 @@ export interface ConfigOpts { slRules?: any; slConfig?: any; files: { - include: string; + include: string | string[]; ignore?: string; }; syntax: { diff --git a/src/types/sass-lint.d.ts b/src/types/sass-lint.d.ts index f904a05f..bae7c0c0 100644 --- a/src/types/sass-lint.d.ts +++ b/src/types/sass-lint.d.ts @@ -37,7 +37,7 @@ declare module 'sass-lint' { 'cache-config'?: boolean; }; files: { - include: string; + include: string | string[]; }; rules: Ruleset; } diff --git a/test/helpers/resolve.ts b/test/helpers/resolve.ts index 77bce41d..a9aeb67c 100644 --- a/test/helpers/resolve.ts +++ b/test/helpers/resolve.ts @@ -9,7 +9,7 @@ const path = require('path'); import { Node, parse } from 'gonzales-pe-sl'; import { LintOpts, lintText, Ruleset } from 'sass-lint'; export interface MockLintConfigParams { - pattern?: string; + pattern?: string | string[]; lintRules: Ruleset; } @@ -35,14 +35,14 @@ export function createMockLintOptions({ } export function* resolvePattern( - pattern: string, + pattern: string | string[], lintRules: Ruleset, logger: ILogger, ): IterableIterator { const configOptions: ConfigOpts = { logger, files: { - include: pattern, + include: typeof pattern === 'string' ? [pattern] : pattern, }, resolvers: { [Object.keys(lintRules)[0]]: 1 }, syntax: { diff --git a/test/src/sass-lint-auto-fix.ts b/test/src/sass-lint-auto-fix.ts index 3a79e030..a34b9282 100644 --- a/test/src/sass-lint-auto-fix.ts +++ b/test/src/sass-lint-auto-fix.ts @@ -140,4 +140,32 @@ describe('sass-lint-auto-fix', () => { const emptyResolutionSet = [...slaf(customSlConfig)]; expect(emptyResolutionSet.length).toEqual(0); }); + + it('accepts a list of files for include with permissive lint pattern', () => { + const logger = createLogger({ silentEnabled: true }); + + const options: Partial = { + logger, + files: { + include: [ + 'test/sass/attribute-quotes.sass', + 'test/sass/attribute-quotes.scss', + ], + }, + resolvers: { 'attribute-quotes': 1 }, + syntax: { + include: ['scss', 'sass'], + }, + }; + + const slaf = autoFixSassFactory(options as ConfigOpts); + const resolutions = [ + ...slaf({ + options: {}, + files: { include: '**/*.s(a|c)ss' }, + rules: { 'attribute-quotes': 1 }, + } as LintOpts), + ]; + expect(resolutions.length).toEqual(2); + }); });