Skip to content

Commit

Permalink
Another --fix proof-of-concept
Browse files Browse the repository at this point in the history
  • Loading branch information
David Clark committed Apr 2, 2017
1 parent 1bcfc00 commit 0e0ade4
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 30 deletions.
2 changes: 2 additions & 0 deletions decls/stylelint.js
Expand Up @@ -36,6 +36,7 @@ export type stylelint$options = {
reportNeedlessDisables?: boolean,
syntax?: stylelint$syntaxes,
customSyntax?: string,
fix?: boolean
}

export type stylelint$internalApi = {
Expand Down Expand Up @@ -115,4 +116,5 @@ export type stylelint$standaloneOptions = {
customSyntax?: string,
formatter?: "json" | "string" | "verbose" | Function,
allowEmptyInput?: boolean,
fix?: booean
}
36 changes: 32 additions & 4 deletions jest-setup.js
Expand Up @@ -41,12 +41,20 @@ global.testRule = (rule, schema) => {
passingTestCases.forEach((testCase) => {
const spec = (testCase.only) ? it.only : it
spec(testCase.description || "no description", () => {
return stylelint({
const options = {
code: testCase.code,
config: stylelintConfig,
syntax: schema.syntax,
}).then((output) => {
}
return stylelint(options).then((output) => {
expect(output.results[0].warnings).toEqual([])
if (!schema.fix) return

// Check the fix
return stylelint(Object.assign({ fix: true }, options)).then((output) => {
const fixedCode = getOutputCss(output)
expect(fixedCode).toBe(testCase.code)
})
})
})
})
Expand All @@ -58,11 +66,12 @@ global.testRule = (rule, schema) => {
schema.reject.forEach((testCase) => {
const spec = (testCase.only) ? it.only : it
spec(testCase.description || "no description", () => {
return stylelint({
const options = {
code: testCase.code,
config: stylelintConfig,
syntax: schema.syntax,
}).then((output) => {
}
return stylelint(options).then((output) => {
const warning = output.results[0].warnings[0]

expect(testCase).toHaveMessage()
Expand All @@ -76,10 +85,29 @@ global.testRule = (rule, schema) => {
if (testCase.column !== undefined) {
expect(_.get(warning, "column")).toBe(testCase.column)
}

if (!schema.fix) return

if (!testCase.fixed) {
throw new Error("If using { fix: true } in test schema, all reject cases must have { fixed: .. }")
}

// Check the fix
return stylelint(Object.assign({ fix: true }, options)).then((output) => {
const fixedCode = getOutputCss(output)
expect(fixedCode).toBe(testCase.fixed)
})
})
})
})
})
}
})
}

function getOutputCss(output) {
const result = output.results[0]._postcssResult
return result.root.toString(result.opts.syntax)
// Less needs us to manually strip whitespace at the end of single-line comments ¯\_(ツ)_/¯
.replace(/(\n?\s*\/\/.*?)[ \t]*(\r?\n)/g, "$1$2")
}
21 changes: 15 additions & 6 deletions lib/cli.js
Expand Up @@ -35,6 +35,7 @@ const minimistOptions = {
"no-color",
"quiet",
"version",
"fix",
],
}

Expand Down Expand Up @@ -82,6 +83,10 @@ const meowOptions = {
If you do not specify a syntax, non-standard syntaxes will be
automatically inferred by the file extensions .scss, .less, and .sss.
--fix
Automatically fix violations of certain rules.
--custom-syntax
Module name or path to a JS file exporting a PostCSS-compatible syntax.
Expand All @@ -93,23 +98,23 @@ const meowOptions = {
--ignore-disables, --id
Ignore styleline-disable comments.
--cache [default: false]
Store the info about processed files in order to only operate on the
changed ones the next time you run stylelint. By default, the cache
Store the info about processed files in order to only operate on the
changed ones the next time you run stylelint. By default, the cache
is stored in "./.stylelintcache". To adjust this, use --cache-location.
--cache-location [default: '.stylelintcache']
Path to a file or directory to be used for the cache location.
Default is "./.stylelintcache". If a directory is specified, a cache
Default is "./.stylelintcache". If a directory is specified, a cache
file will be created inside the specified folder, with a name derived
from a hash of the current working directory.
If the directory for the cache does not exist, make sure you add a trailing "/"
If the directory for the cache does not exist, make sure you add a trailing "/"
on \*nix systems or "\" on Windows. Otherwise the path will be assumed to be a file.
--formatter, -f [default: "string"]
Expand Down Expand Up @@ -209,6 +214,10 @@ if (cli.flags.cacheLocation) {
optionsBase.cacheLocation = cli.flags.cacheLocation
}

if (cli.flags.fix) {
optionsBase.fix = cli.flags.fix
}

const reportNeedlessDisables = cli.flags.reportNeedlessDisables

if (reportNeedlessDisables) {
Expand Down
14 changes: 11 additions & 3 deletions lib/lintSource.js
@@ -1,14 +1,16 @@
/* @flow */
"use strict"

const _ = require("lodash")
const assignDisabledRanges = require("./assignDisabledRanges")
const configurationError = require("./utils/configurationError")
const path = require("path")
const os = require("os")
const ruleDefinitions = require("./rules")

// Run stylelint on a PostCSS Result, either one that is provided
// or one that we create
module.exports = function (
module.exports = function lintSource(
stylelint/*: stylelint$internalApi*/,
options/*: {
code?: string,
Expand Down Expand Up @@ -79,12 +81,15 @@ function lintPostcssResult(
stylelint/*: stylelint$internalApi*/,
postcssResult/*: Object*/,
config/*: stylelint$config*/
)/*: Promise<>*/ {
)/*: Promise<Array<*>>*/ {
postcssResult.stylelint = postcssResult.stylelint || {}
postcssResult.stylelint.ruleSeverities = {}
postcssResult.stylelint.customMessages = {}
postcssResult.stylelint.quiet = config.quiet

const newlineMatch = postcssResult.toString().match(/\r?\n/)
const newline = newlineMatch ? newlineMatch[0] : os.EOL

const postcssRoot = postcssResult.root
assignDisabledRanges(postcssRoot, postcssResult)
if (stylelint._options.reportNeedlessDisables || stylelint._options.ignoreDisables) {
Expand Down Expand Up @@ -119,7 +124,10 @@ function lintPostcssResult(
postcssResult.stylelint.customMessages[ruleName] = _.get(secondaryOptions, "message")

const performRule = Promise.resolve().then(() => {
return ruleFunction(primaryOption, secondaryOptions)(postcssRoot, postcssResult)
return ruleFunction(primaryOption, secondaryOptions, {
fix: stylelint._options.fix,
newline,
})(postcssRoot, postcssResult)
})
performRules.push(performRule)
})
Expand Down
24 changes: 24 additions & 0 deletions lib/rules/at-rule-name-case/__tests__/index.js
Expand Up @@ -10,6 +10,7 @@ testRule(rule, {
ruleName,
config: ["lower"],
skipBasicChecks: true,
fix: true,

accept: [ {
code: "@charset 'UTF-8';",
Expand Down Expand Up @@ -43,56 +44,67 @@ testRule(rule, {

reject: [ {
code: "@Charset 'UTF-8';",
fixed: "@charset 'UTF-8';",
message: messages.expected("Charset", "charset"),
line: 1,
column: 1,
}, {
code: "@cHaRsEt 'UTF-8';",
fixed: "@charset 'UTF-8';",
message: messages.expected("cHaRsEt", "charset"),
line: 1,
column: 1,
}, {
code: "@CHARSET 'UTF-8';",
fixed: "@charset 'UTF-8';",
message: messages.expected("CHARSET", "charset"),
line: 1,
column: 1,
}, {
code: "@Media screen {}",
fixed: "@media screen {}",
message: messages.expected("Media", "media"),
line: 1,
column: 1,
}, {
code: "@mEdIa screen {}",
fixed: "@media screen {}",
message: messages.expected("mEdIa", "media"),
line: 1,
column: 1,
}, {
code: "@MEDIA screen {}",
fixed: "@media screen {}",
message: messages.expected("MEDIA", "media"),
line: 1,
column: 1,
}, {
code: "@media only screen and (min-width: 415px) { @Keyframes pace-anim { 100% { opacity: 0; } } }",
fixed: "@media only screen and (min-width: 415px) { @keyframes pace-anim { 100% { opacity: 0; } } }",
message: messages.expected("Keyframes", "keyframes"),
line: 1,
column: 45,
}, {
code: "@media only screen and (min-width: 415px) { @kEyFrAmEs pace-anim { 100% { opacity: 0; } } }",
fixed: "@media only screen and (min-width: 415px) { @keyframes pace-anim { 100% { opacity: 0; } } }",
message: messages.expected("kEyFrAmEs", "keyframes"),
line: 1,
column: 45,
}, {
code: "@media only screen and (min-width: 415px) { @KEYFRAMES pace-anim { 100% { opacity: 0; } } }",
fixed: "@media only screen and (min-width: 415px) { @keyframes pace-anim { 100% { opacity: 0; } } }",
message: messages.expected("KEYFRAMES", "keyframes"),
line: 1,
column: 45,
}, {
code: "@-WEBKIT-keyframes { 0% { top: 0; } }",
fixed: "@-webkit-keyframes { 0% { top: 0; } }",
message: messages.expected("-WEBKIT-keyframes", "-webkit-keyframes"),
line: 1,
column: 1,
}, {
code: "@-WEBKIT-KEYFRAMES { 0% { top: 0; } }",
fixed: "@-webkit-keyframes { 0% { top: 0; } }",
message: messages.expected("-WEBKIT-KEYFRAMES", "-webkit-keyframes"),
line: 1,
column: 1,
Expand All @@ -103,6 +115,7 @@ testRule(rule, {
ruleName,
config: ["upper"],
skipBasicChecks: true,
fix: true,

accept: [ {
code: "@CHARSET 'UTF-8';",
Expand Down Expand Up @@ -136,56 +149,67 @@ testRule(rule, {

reject: [ {
code: "@Charset 'UTF-8';",
fixed: "@CHARSET 'UTF-8';",
message: messages.expected("Charset", "CHARSET"),
line: 1,
column: 1,
}, {
code: "@cHaRsEt 'UTF-8';",
fixed: "@CHARSET 'UTF-8';",
message: messages.expected("cHaRsEt", "CHARSET"),
line: 1,
column: 1,
}, {
code: "@charset 'UTF-8';",
fixed: "@CHARSET 'UTF-8';",
message: messages.expected("charset", "CHARSET"),
line: 1,
column: 1,
}, {
code: "@Media screen {}",
fixed: "@MEDIA screen {}",
message: messages.expected("Media", "MEDIA"),
line: 1,
column: 1,
}, {
code: "@mEdIa screen {}",
fixed: "@MEDIA screen {}",
message: messages.expected("mEdIa", "MEDIA"),
line: 1,
column: 1,
}, {
code: "@media screen {}",
fixed: "@MEDIA screen {}",
message: messages.expected("media", "MEDIA"),
line: 1,
column: 1,
}, {
code: "@MEDIA only screen and (min-width: 415px) { @Keyframes pace-anim { 100% { opacity: 0; } } }",
fixed: "@MEDIA only screen and (min-width: 415px) { @KEYFRAMES pace-anim { 100% { opacity: 0; } } }",
message: messages.expected("Keyframes", "KEYFRAMES"),
line: 1,
column: 45,
}, {
code: "@MEDIA only screen and (min-width: 415px) { @kEyFrAmEs pace-anim { 100% { opacity: 0; } } }",
fixed: "@MEDIA only screen and (min-width: 415px) { @KEYFRAMES pace-anim { 100% { opacity: 0; } } }",
message: messages.expected("kEyFrAmEs", "KEYFRAMES"),
line: 1,
column: 45,
}, {
code: "@MEDIA only screen and (min-width: 415px) { @keyframes pace-anim { 100% { opacity: 0; } } }",
fixed: "@MEDIA only screen and (min-width: 415px) { @KEYFRAMES pace-anim { 100% { opacity: 0; } } }",
message: messages.expected("keyframes", "KEYFRAMES"),
line: 1,
column: 45,
}, {
code: "@-webkit-KEYFRAMES { 0% { top: 0; } }",
fixed: "@-WEBKIT-KEYFRAMES { 0% { top: 0; } }",
message: messages.expected("-webkit-KEYFRAMES", "-WEBKIT-KEYFRAMES"),
line: 1,
column: 1,
}, {
code: "@-webkit-keyframes { 0% { top: 0; } }",
fixed: "@-WEBKIT-KEYFRAMES { 0% { top: 0; } }",
message: messages.expected("-webkit-keyframes", "-WEBKIT-KEYFRAMES"),
line: 1,
column: 1,
Expand Down
7 changes: 6 additions & 1 deletion lib/rules/at-rule-name-case/index.js
Expand Up @@ -10,7 +10,7 @@ const messages = ruleMessages(ruleName, {
expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`,
})

const rule = function (expectation) {
const rule = function (expectation, options, context) {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: expectation,
Expand All @@ -34,6 +34,11 @@ const rule = function (expectation) {
return
}

if (context.fix) {
atRule.name = expectedName
return
}

report({
message: messages.expected(name, expectedName),
node: atRule,
Expand Down

0 comments on commit 0e0ade4

Please sign in to comment.